home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HPAVC
/
HPAVC CD-ROM.iso
/
ANIVGA.ZIP
/
ANIVGA.NEW
< prev
next >
Wrap
Text File
|
1994-09-22
|
247KB
|
7,554 lines
{$A+,B-,D+,L+,N-,E-,O-,R-,S-,V-,G-,F-,I-,X-}
{$M 16384,0,655360}
{Programtool for the realization of fast animations on the VGA-graphic- }
{card by: Kai Rohrbacher, 1988-1991, Turbo-Pascal 6.0 }
{ Features: }
{ - flickerfree animation by using page-flipping, watching retrace }
{ signals and usage of a special 256 color mode of the VGA }
{ - sprite movement in any pixel-increments you want }
{ - arbitrary background image for the animation }
{ - full use of the VGA's 256-color mode }
{ - several sprite display methods available: }
{ - sprites can be declared to be transparent with regard to the background}
{ or other sprites pixel by pixel }
{ - sprites can change their color depending on the underlying background }
{ image (-> shadow function) }
{ - pixel precise hit-detection routine for sprite collisions built in }
{ - correct clipping of sprites at the screen boundaries when moving on- or }
{ offscreen }
{ - up to 32767 different sprites (default: 1000) }
{ - up to 32767 sprites may be simultaneously active (default: 500) }
{ - maximal size of each sprite = 64k }
{ - maximal size of all loaded sprites only restricted by available memory }
{ - works with virtual coordinates in the range -16000..+16000, thus }
{ simple creation of horizontal/vertical "scrolling" applications }
{ - scrolling background image, too }
{ - several supporting routines to: draw lines (with built in clipping- }
{ algorithm), points and graphic-text (incl. clipping), automatic heap }
{ management for storing/loading sprites, handling background images, }
{ changing sprite display modes at runtime, adjustment for different }
{ CPU-clocks, ... }
UNIT ANIVGA;
INTERFACE
USES CRT,DOS;
CONST ANIVGAVersion=11; {version number}
NMAX=499;
XMAX=319;
YMAX=199;
LoadMAX=1000; {max. number of simultaneously active sprites }
LINESIZE=(XMAX+1) DIV 4; {size of one row=80 bytes }
PAGESIZE=(YMAX+1)*LINESIZE; {200 rows at 320/4 bytes each}
BACKGNDPAGE=2;
SCROLLPAGE=3;
STATIC=0; {constants for background mode }
SCROLLING=1;
MaxTiles=10000; {max. number of background tiles }
StartVirtualX:INTEGER=0; {upper left image corner }
StartVirtualY:INTEGER=0;
{supported display modes for sprites: }
Display_NORMAL=0; {normal : transparent for color 0 }
Display_FAST =1; {fast : don't take background into account}
Display_SHADOW=2; {shadow : color lookup driven by background data}
Display_SHADOWEXACT=3; {color 0 is transparent for shadows, too }
Display_UNKNOWN=255;{error value}
{error codes of the animation package: }
Err_None=0;
Err_NotEnoughMemory=1;
Err_FileIO=2;
Err_InvalidSpriteNumber=3;
Err_NoSprite=4;
Err_InvalidPageNumber=5;
Err_NoVGA=6;
Err_NoPicture=7;
Err_InvalidPercentage=8;
Err_NoTile=9;
Err_InvalidTileNumber=10;
Err_InvalidCoordinates=11;
Err_BackgroundToBig=12;
Err_InvalidMode=13;
Err_InvalidSpriteLoadNumber=14;
Err_NoPalette=15;
Err_PaletteWontFit=16;
Err_InvalidFade=17;
TYPE FontChar=ARRAY[0..5] OF BYTE;
Font=ARRAY[0..255] OF Fontchar;
FontOrient=(horizontal,vertical); {possible text directions }
CONST GraphTextOrientation:FontOrient=horizontal; {actual output direction }
GraphTextColor:BYTE=white; {actual text color }
GraphTextBackground:BYTE=white;
FontHeight=6;
FontWidth=6;
TYPE Table=ARRAY[0..NMAX] OF INTEGER;
ColorTable=ARRAY[0..255] OF BYTE;
TYPE PaletteEntry=RECORD red,green,blue:BYTE END;
Palette=ARRAY[0..255] OF PaletteEntry;
PalettePtr=^Palette;
CONST DefaultColors:Palette= {default palette colors of 256color mode}
( {read out by using the VGA-BIOS-calls: }
(red: 0; green: 0; blue: 0), { MOV AX,1017h ;read palette registers}
(red: 0; green: 0; blue: 42), { XOR BX,BX ;start with color 0 }
(red: 0; green: 42; blue: 0), { MOV CX,100h ;all 256 colors }
(red: 0; green: 42; blue: 42), { LES DX,Ziel ;to ES:DX }
(red: 42; green: 0; blue: 0), { INT 10h }
(red: 42; green: 0; blue: 42), {Attention! These values can/could be }
(red: 42; green: 21; blue: 0), {read out directly from the VGA card not}
(red: 42; green: 42; blue: 42), {until a graphic mode has been activated; }
(red: 21; green: 21; blue: 21), {thus, here they were defined "static"}
(red: 21; green: 21; blue: 63),
(red: 21; green: 63; blue: 21),
(red: 21; green: 63; blue: 63),
(red: 63; green: 21; blue: 21),
(red: 63; green: 21; blue: 63),
(red: 63; green: 63; blue: 21),
(red: 63; green: 63; blue: 63),
(red: 0; green: 0; blue: 0),
(red: 5; green: 5; blue: 5),
(red: 8; green: 8; blue: 8),
(red: 11; green: 11; blue: 11),
(red: 14; green: 14; blue: 14),
(red: 17; green: 17; blue: 17),
(red: 20; green: 20; blue: 20),
(red: 24; green: 24; blue: 24),
(red: 28; green: 28; blue: 28),
(red: 32; green: 32; blue: 32),
(red: 36; green: 36; blue: 36),
(red: 40; green: 40; blue: 40),
(red: 45; green: 45; blue: 45),
(red: 50; green: 50; blue: 50),
(red: 56; green: 56; blue: 56),
(red: 63; green: 63; blue: 63),
(red: 0; green: 0; blue: 63),
(red: 16; green: 0; blue: 63),
(red: 31; green: 0; blue: 63),
(red: 47; green: 0; blue: 63),
(red: 63; green: 0; blue: 63),
(red: 63; green: 0; blue: 47),
(red: 63; green: 0; blue: 31),
(red: 63; green: 0; blue: 16),
(red: 63; green: 0; blue: 0),
(red: 63; green: 16; blue: 0),
(red: 63; green: 31; blue: 0),
(red: 63; green: 47; blue: 0),
(red: 63; green: 63; blue: 0),
(red: 47; green: 63; blue: 0),
(red: 31; green: 63; blue: 0),
(red: 16; green: 63; blue: 0),
(red: 0; green: 63; blue: 0),
(red: 0; green: 63; blue: 16),
(red: 0; green: 63; blue: 31),
(red: 0; green: 63; blue: 47),
(red: 0; green: 63; blue: 63),
(red: 0; green: 47; blue: 63),
(red: 0; green: 31; blue: 63),
(red: 0; green: 16; blue: 63),
(red: 31; green: 31; blue: 63),
(red: 39; green: 31; blue: 63),
(red: 47; green: 31; blue: 63),
(red: 55; green: 31; blue: 63),
(red: 63; green: 31; blue: 63),
(red: 63; green: 31; blue: 55),
(red: 63; green: 31; blue: 47),
(red: 63; green: 31; blue: 39),
(red: 63; green: 31; blue: 31),
(red: 63; green: 39; blue: 31),
(red: 63; green: 47; blue: 31),
(red: 63; green: 55; blue: 31),
(red: 63; green: 63; blue: 31),
(red: 55; green: 63; blue: 31),
(red: 47; green: 63; blue: 31),
(red: 39; green: 63; blue: 31),
(red: 31; green: 63; blue: 31),
(red: 31; green: 63; blue: 39),
(red: 31; green: 63; blue: 47),
(red: 31; green: 63; blue: 55),
(red: 31; green: 63; blue: 63),
(red: 31; green: 55; blue: 63),
(red: 31; green: 47; blue: 63),
(red: 31; green: 39; blue: 63),
(red: 45; green: 45; blue: 63),
(red: 49; green: 45; blue: 63),
(red: 54; green: 45; blue: 63),
(red: 58; green: 45; blue: 63),
(red: 63; green: 45; blue: 63),
(red: 63; green: 45; blue: 58),
(red: 63; green: 45; blue: 54),
(red: 63; green: 45; blue: 49),
(red: 63; green: 45; blue: 45),
(red: 63; green: 49; blue: 45),
(red: 63; green: 54; blue: 45),
(red: 63; green: 58; blue: 45),
(red: 63; green: 63; blue: 45),
(red: 58; green: 63; blue: 45),
(red: 54; green: 63; blue: 45),
(red: 49; green: 63; blue: 45),
(red: 45; green: 63; blue: 45),
(red: 45; green: 63; blue: 49),
(red: 45; green: 63; blue: 54),
(red: 45; green: 63; blue: 58),
(red: 45; green: 63; blue: 63),
(red: 45; green: 58; blue: 63),
(red: 45; green: 54; blue: 63),
(red: 45; green: 49; blue: 63),
(red: 0; green: 0; blue: 28),
(red: 7; green: 0; blue: 28),
(red: 14; green: 0; blue: 28),
(red: 21; green: 0; blue: 28),
(red: 28; green: 0; blue: 28),
(red: 28; green: 0; blue: 21),
(red: 28; green: 0; blue: 14),
(red: 28; green: 0; blue: 7),
(red: 28; green: 0; blue: 0),
(red: 28; green: 7; blue: 0),
(red: 28; green: 14; blue: 0),
(red: 28; green: 21; blue: 0),
(red: 28; green: 28; blue: 0),
(red: 21; green: 28; blue: 0),
(red: 14; green: 28; blue: 0),
(red: 7; green: 28; blue: 0),
(red: 0; green: 28; blue: 0),
(red: 0; green: 28; blue: 7),
(red: 0; green: 28; blue: 14),
(red: 0; green: 28; blue: 21),
(red: 0; green: 28; blue: 28),
(red: 0; green: 21; blue: 28),
(red: 0; green: 14; blue: 28),
(red: 0; green: 7; blue: 28),
(red: 14; green: 14; blue: 28),
(red: 17; green: 14; blue: 28),
(red: 21; green: 14; blue: 28),
(red: 24; green: 14; blue: 28),
(red: 28; green: 14; blue: 28),
(red: 28; green: 14; blue: 24),
(red: 28; green: 14; blue: 21),
(red: 28; green: 14; blue: 17),
(red: 28; green: 14; blue: 14),
(red: 28; green: 17; blue: 14),
(red: 28; green: 21; blue: 14),
(red: 28; green: 24; blue: 14),
(red: 28; green: 28; blue: 14),
(red: 24; green: 28; blue: 14),
(red: 21; green: 28; blue: 14),
(red: 17; green: 28; blue: 14),
(red: 14; green: 28; blue: 14),
(red: 14; green: 28; blue: 17),
(red: 14; green: 28; blue: 21),
(red: 14; green: 28; blue: 24),
(red: 14; green: 28; blue: 28),
(red: 14; green: 24; blue: 28),
(red: 14; green: 21; blue: 28),
(red: 14; green: 17; blue: 28),
(red: 20; green: 20; blue: 28),
(red: 22; green: 20; blue: 28),
(red: 24; green: 20; blue: 28),
(red: 26; green: 20; blue: 28),
(red: 28; green: 20; blue: 28),
(red: 28; green: 20; blue: 26),
(red: 28; green: 20; blue: 24),
(red: 28; green: 20; blue: 22),
(red: 28; green: 20; blue: 20),
(red: 28; green: 22; blue: 20),
(red: 28; green: 24; blue: 20),
(red: 28; green: 26; blue: 20),
(red: 28; green: 28; blue: 20),
(red: 26; green: 28; blue: 20),
(red: 24; green: 28; blue: 20),
(red: 22; green: 28; blue: 20),
(red: 20; green: 28; blue: 20),
(red: 20; green: 28; blue: 22),
(red: 20; green: 28; blue: 24),
(red: 20; green: 28; blue: 26),
(red: 20; green: 28; blue: 28),
(red: 20; green: 26; blue: 28),
(red: 20; green: 24; blue: 28),
(red: 20; green: 22; blue: 28),
(red: 0; green: 0; blue: 16),
(red: 4; green: 0; blue: 16),
(red: 8; green: 0; blue: 16),
(red: 12; green: 0; blue: 16),
(red: 16; green: 0; blue: 16),
(red: 16; green: 0; blue: 12),
(red: 16; green: 0; blue: 8),
(red: 16; green: 0; blue: 4),
(red: 16; green: 0; blue: 0),
(red: 16; green: 4; blue: 0),
(red: 16; green: 8; blue: 0),
(red: 16; green: 12; blue: 0),
(red: 16; green: 16; blue: 0),
(red: 12; green: 16; blue: 0),
(red: 8; green: 16; blue: 0),
(red: 4; green: 16; blue: 0),
(red: 0; green: 16; blue: 0),
(red: 0; green: 16; blue: 4),
(red: 0; green: 16; blue: 8),
(red: 0; green: 16; blue: 12),
(red: 0; green: 16; blue: 16),
(red: 0; green: 12; blue: 16),
(red: 0; green: 8; blue: 16),
(red: 0; green: 4; blue: 16),
(red: 8; green: 8; blue: 16),
(red: 10; green: 8; blue: 16),
(red: 12; green: 8; blue: 16),
(red: 14; green: 8; blue: 16),
(red: 16; green: 8; blue: 16),
(red: 16; green: 8; blue: 14),
(red: 16; green: 8; blue: 12),
(red: 16; green: 8; blue: 10),
(red: 16; green: 8; blue: 8),
(red: 16; green: 10; blue: 8),
(red: 16; green: 12; blue: 8),
(red: 16; green: 14; blue: 8),
(red: 16; green: 16; blue: 8),
(red: 14; green: 16; blue: 8),
(red: 12; green: 16; blue: 8),
(red: 10; green: 16; blue: 8),
(red: 8; green: 16; blue: 8),
(red: 8; green: 16; blue: 10),
(red: 8; green: 16; blue: 12),
(red: 8; green: 16; blue: 14),
(red: 8; green: 16; blue: 16),
(red: 8; green: 14; blue: 16),
(red: 8; green: 12; blue: 16),
(red: 8; green: 10; blue: 16),
(red: 11; green: 11; blue: 16),
(red: 12; green: 11; blue: 16),
(red: 13; green: 11; blue: 16),
(red: 15; green: 11; blue: 16),
(red: 16; green: 11; blue: 16),
(red: 16; green: 11; blue: 15),
(red: 16; green: 11; blue: 13),
(red: 16; green: 11; blue: 12),
(red: 16; green: 11; blue: 11),
(red: 16; green: 12; blue: 11),
(red: 16; green: 13; blue: 11),
(red: 16; green: 15; blue: 11),
(red: 16; green: 16; blue: 11),
(red: 15; green: 16; blue: 11),
(red: 13; green: 16; blue: 11),
(red: 12; green: 16; blue: 11),
(red: 11; green: 16; blue: 11),
(red: 11; green: 16; blue: 12),
(red: 11; green: 16; blue: 13),
(red: 11; green: 16; blue: 15),
(red: 11; green: 16; blue: 16),
(red: 11; green: 15; blue: 16),
(red: 11; green: 13; blue: 16),
(red: 11; green: 12; blue: 16),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0)
);
VAR Error:BYTE; {global error variable }
SpriteN:Table;
SpriteX:Table;
SpriteY:Table;
NextSprite:ARRAY[0..LoadMAX] OF WORD;
SPRITEAD:ARRAY[0..LoadMAX] OF WORD;
PAGE,PAGEADR,SCROLLADR,BACKGNDADR:WORD;
Color:BYTE; {drawing color for lines }
ActualColors:Palette; {actual color palette}
was_cut:BOOLEAN; {TRUE/FALSE, if "GetImage" had to clip image }
left_cut, {variables set by "GetImage"; if "was_cut" has}
right_cut, {been set to TRUE, they will report, where and }
top_cut, {and how much of the image had to been clipped }
bottom_cut:WORD; {off }
BackgroundMode:BYTE;
BackTile:ARRAY[0..MaxTiles] OF BYTE; {tile memory }
XTiles,YTiles:INTEGER; {width, height of defined area }
BackX1,BackY1,BackX2,BackY2:INTEGER; {coordinates of the defined area }
CONST Fade_Squares =0;
Fade_Moiree1 =1;
Fade_Moiree2 =2;
Fade_Moiree3 =3;
Fade_Moiree4 =4;
Fade_Moiree5 =5;
Fade_Moiree6 =6;
Fade_Moiree7 =7;
Fade_Moiree8 =8;
Fade_Moiree9 =9;
Fade_Moiree10=10;
Fade_Moiree11=11;
Fade_Moiree12=12;
Fade_Moiree13=13;
Fade_Moiree14=14;
Fade_SweepInFromTop=15;
Fade_SweepInFromBottom=16;
Fade_SweepInFromLeft=17;
Fade_SweepInFromRight=18;
Fade_ScrollInFromTop=19;
Fade_ScrollInFromBottom=20;
Fade_ScrollInFromLeft=21;
Fade_ScrollInFromRight=22;
Fade_Circles =23;
PROCEDURE ShadowTab;
PROCEDURE SetShadowTab(brightness:BYTE);
PROCEDURE SetPalette(pal:Palette; update:BOOLEAN);
PROCEDURE GetPalette(VAR pal:Palette);
FUNCTION LoadPalette(name:String; number:BYTE; VAR pal:Palette):WORD;
PROCEDURE SetCycleTime(milliseconds:WORD);
PROCEDURE SetSpriteCycle(nr,len:WORD);
FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:BYTE):POINTER;
PROCEDURE PutImage(x,y:INTEGER; p:POINTER; pa:BYTE);
PROCEDURE FreeImageMem(p:POINTER);
PROCEDURE InitGraph;
PROCEDURE Screen(pa:BYTE);
PROCEDURE Line(x1,y1,x2,y2:INTEGER; pa:BYTE);
PROCEDURE BackgroundLine(x1,y1,x2,y2:INTEGER);
FUNCTION GetPixel(x,y:INTEGER):BYTE;
FUNCTION BackgroundGetPixel(x,y:INTEGER):BYTE;
FUNCTION PageGetPixel(x,y:INTEGER; pa:BYTE):BYTE;
PROCEDURE PutPixel(x,y:INTEGER; color:Byte);
PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte);
PROCEDURE PagePutPixel(x,y:INTEGER; color,pa:Byte);
PROCEDURE OutTextXY(x,y:INTEGER; pa:BYTE; s:STRING);
PROCEDURE BackgroundOutTextXY(x,y:INTEGER; s:STRING);
FUNCTION Hitdetect(s1,s2:INTEGER):BOOLEAN;
PROCEDURE Animate;
FUNCTION LoadSprite(name:String; number:WORD):WORD;
FUNCTION LoadTile(name:STRING; number:BYTE):WORD;
PROCEDURE SetBackgroundScrollRange(x1,y1,x2,y2:INTEGER);
PROCEDURE SetBackgroundMode(mode:BYTE);
PROCEDURE PutTile(x,y:INTEGER; TileNr:BYTE);
PROCEDURE SetOffscreenTile(TileNr:BYTE);
PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
FUNCTION GetModeByte(Sp:WORD):BYTE;
PROCEDURE FillPage(pa,color:Byte);
PROCEDURE FillBackground(color:BYTE);
PROCEDURE GetBackgroundFromPage(pa:Byte);
PROCEDURE WritePage(name:STRING; pa:BYTE);
PROCEDURE LoadPage(name:STRING; pa:BYTE);
PROCEDURE WriteBackgroundPage(name:STRING);
PROCEDURE LoadBackgroundPage(name:STRING);
PROCEDURE FadeIn(pa,ti,style:WORD);
PROCEDURE InitRoutines;
PROCEDURE CloseRoutines;
FUNCTION GetErrorMessage:STRING;
{--------------------------------------------------------------------------}
IMPLEMENTATION
CONST StartIndex=0;
EndIndex=StartIndex+3;
{offset addresses of the graphic pages (in segment $A000):}
Offset_Adr:Array[StartIndex..EndIndex] OF WORD=($0000,$3E80,$7D00,$BB80);
{segment addresses of graphic pages (with offset = 0):}
Segment_Adr:ARRAY[StartIndex..EndIndex] OF WORD=($A000,$A3E8,$A7D0,$ABB8);
{sprite header format:}
{ 0..1 DW offset_ptr_to_plane0_data}
{ 2..3 DW offset_ptr_to_plane1_data}
{ 4..5 DW offset_ptr_to_plane2_data}
{ 6..7 DW offset_ptr_to_plane3_data}
{ 8..9 DW width (in groups of 4) (in German: "Breite")}
{ 10..11 DW heigth in rows (in German: "Hoehe")}
{ 12..15 DB 1,2,4,8 ; translate-table used for port-accesses }
{ 16..17 DW SpriteLength ; length of this sprite in bytes}
{ ; now follows the scratch area reserved for temporary}
{ ; variables: ("a|b" = used for a and b)}
{ 18..19 DW ? ; licutoff_ | hit1xfirst}
{ 20..21 DW ? ; zeilenadr | hit1yfirst}
{ 22..23 DW ? ; bildx | hit2xfirst}
{ 24..25 DW ? ; yoffset_ | hit2yfirst}
{ 26..27 DW ? ; end_min_start | ueberlappx_1}
{ 28..29 DW ? ; | ueberlappy_1}
{ 30..31 DW ? ; | x1}
{ 32..33 DW ? ; | x2}
{ 34..35 DW ? ; | y1}
{ 36..37 DW ? ; | y2}
{ 38..39 DB 'K','R' ; tag used for sprites}
{ 40 DB 1 ; version number}
{ 41 DB 0 ; display mode used for sprite}
{ 42..43 DW offset_ptr_to_left_boundary_data}
{ 44..45 DW offset_ptr_to_right_boundary_data}
{ 46..47 DW offset_ptr_to_top_boundary_data}
{ 48..49 DW offset_ptr_to_bottom_boundary_data}
{ 50..?? DB data }
{ for ex.: xxrxxxxx, with: r=red=40, g=green=45, b=blue=35, x=white=30}
{ xrgrxxxx}
{ rbgbrxxx}
{ }
{addresses of important values inside the sprite header: }
Left=42;
Right=44;
Top=46;
Bottom=48;
Breite=8;
Hoehe=10;
Translate=12;
SpriteLength=16;
Kennung=38;
Version=40;
Modus=41;
{addresses of temporary variables for the drawing routines: }
licutoff_=18;
zeilenadr=20;
bildx=22;
yoffset_=24;
end_min_start=26;
{addresses of temporary variables for the collision detection routine:}
hit1xfirst=18;
hit1yfirst=20;
hit2xfirst=22;
hit2yfirst=24;
ueberlappx_1=26;
ueberlappy_1=28;
x1=30;
x2=32;
y1=34;
y2=36;
TranslateTab:ARRAY[0..3] OF BYTE=(1,2,4,8); {For mask addressing }
PICHeader:STRING[3]='PIC'; {tag in picture files }
Schatten :BYTE=70; {default brightness of shadows }
TYPE SpriteHeader= RECORD
Zeiger_auf_Plane:Array[0..3] OF Word;
Breite_in_4er_Gruppen:WORD;
Hoehe_in_Zeilen:WORD;
Translate:Array[1..4] OF Byte;
SpriteLength:WORD;
Dummy:Array[1..10] OF Word;
Kennung:ARRAY[1..2] OF CHAR;
Version:BYTE;
Modus:BYTE;
ZeigerL,ZeigerR,ZeigerO,ZeigerU:Word;
END;
CONST FontMask:ARRAY[0..7] OF BYTE=($80,$40,$20,$10,8,4,2,1);
FontData: Font=( {used font: }
( 0, 0, 0, 0, 0, 0), {#0} {selfmade 6x6 font (ugly..)}
( 0,216, 0,248,112, 0), {#1}
(248,168,248,136,216,248), {#2}
( 0, 80,248,248,112, 32), {#3}
( 0, 32,112,248,112, 32), {#4}
( 32,112,216,248, 32,112), {#5}
( 32,112,248,248, 32,112), {#6}
( 0, 32,216,216, 32, 0), {#7}
(248,216,168,168,216,248), {#8}
( 0,112,200,152,112, 0), {#9}
(248,136,168,168,136,248), {#10}
( 56, 24, 32,112,136,112), {#11}
(112,136,112, 32,248, 32), {#12}
( 56, 40, 32, 32,224,224), {#13}
( 0,120, 72,120, 72,216), {#14}
( 0, 32,168, 80,168, 32), {#15}
( 0,128,224,248,224,128), {#16}
( 0, 8, 56,248, 56, 8), {#17}
( 32,112,168,168,112, 32), {#18}
( 0,216,216,216, 0,216), {#19}
( 0,120,168,104, 40, 40), {#20}
( 24, 32, 16, 40,144, 96), {#21}
( 0, 0, 0, 0,248,248), {#22}
( 32,112, 32,112, 32,248), {#23}
( 0, 32,112,248, 32, 32), {#24}
( 0, 32, 32,248,112, 32), {#25}
( 0, 32, 16,248, 16, 32), {#26}
( 0, 32, 64,248, 64, 32), {#27}
( 0, 0,192,248, 0, 0), {#28}
( 0, 0, 80,248, 80, 0), {#29}
( 0, 0, 0, 32,112,248), {#30}
( 0, 0,248,112, 32, 0), {#31}
( 0, 0, 0, 0, 0, 0), { }
( 0, 48, 48, 48, 0, 48), {!}
( 0, 80, 80, 0, 0, 0), {"}
( 0, 80,248, 80,248, 80), {#}
( 32,120,160,112, 40,240), { $}
( 0,200, 16, 32, 64,152), {%}
( 0,112,216,112,152,104), {&}
( 0, 16, 32, 0, 0, 0), {'}
( 0,112,192,192,192,112), {(}
( 0,224, 48, 48, 48,224), {)}
( 0, 80, 32,248, 32, 80), {*}
( 0, 0, 32,248, 32, 0), {+}
( 0, 0, 0, 32, 32, 64), {,}
( 0, 0, 0,248, 0, 0), {-}
( 0, 0, 0, 0, 48, 0), {.}
( 4, 8, 16, 32, 64,128), {/}
( 0,112,152,168,200,112), {0}
( 0, 48,112, 48, 48,120), {1}
( 0,240, 24,112,192,248), {2}
( 0,240, 24,112, 24,240), {3}
( 0,192,208,248, 48, 48), {4}
( 0,248,192,240, 24,240), {5}
( 0,248,128,248,136,248), {6}
( 0,248, 24, 48, 96, 96), {7}
( 0,112,216,112,216,112), {8}
( 0,112,136,120, 8,112), {9}
( 0, 0, 32, 0, 32, 0), {:}
( 0, 0, 32, 0, 32, 64), {;}
( 0, 24, 48, 96, 48, 24), {<}
( 0, 0,248, 0,248, 0), {=}
( 0, 96, 48, 24, 48, 96), {>}
(112,136, 16, 32, 0, 32), {?}
( 0,112,136,184,128,120), {@}
( 0,112,200,248,200,200), {A}
( 0,240,200,240,200,240), {B}
( 0,120,192,192,192,120), {C}
( 0,240,216,200,216,240), {D}
( 0,248,192,240,192,248), {E}
( 0,248,192,240,192,192), {F}
( 0,120,192,216,200,120), {G}
( 0,200,200,248,200,200), {H}
( 0,120, 48, 48, 48,120), {I}
( 0,248, 8, 8,200,112), {J}
( 0,200,208,224,208,200), {K}
( 0,192,192,192,192,248), {L}
( 0,136,216,168,136,136), {M}
( 0,136,200,168,152,136), {N}
( 0,112,200,200,200,112), {O}
( 0,240,200,240,192,192), {P}
( 0, 96,208,208,208,104), {Q}
( 0,240,136,240,208,200), {R}
( 0,248,192,248, 24,248), {S}
( 0,248, 96, 96, 96, 96), {T}
( 0,200,200,200,200,248), {U}
( 0,200,200,200,200, 48), {V}
( 0,136,136,168,248, 80), {W}
( 0,136,216,112,216,136), {X}
( 0,200,200,112, 48, 48), {Y}
( 0,248, 24,112,192,248), {Z}
( 0,120, 96, 96, 96,120), {[}
(128, 64, 32, 16, 8, 4), {\}
( 0,120, 24, 24, 24,120), {]}
( 32, 80,136, 0, 0, 0), {^}
( 0, 0, 0, 0, 0,248), {_}
( 64, 32, 0, 0, 0, 0), {`}
( 0, 0,112,200,200,120), {a}
( 0,128,240,136,136,240), {b}
( 0, 0,120,192,192,120), {c}
( 0, 8,120,136,136,120), {d}
( 0, 0,112,248,128,112), {e}
( 0, 24, 32,120, 32, 32), {f}
( 0,112,136,120, 8,112), {g}
( 0,192,240,200,200,200), {h}
( 48, 0, 48, 48, 48, 48), {i}
( 24, 0, 24, 24,216,112), {j}
( 0,192,208,224,216,216), {k}
( 0, 96, 96, 96, 96, 56), {l}
( 0, 0,208,248,168,136), {m}
( 0, 0,240,200,200,200), {n}
( 0, 0,112,200,200,112), {o}
( 0, 0,240,200,240,192), {p}
( 0, 0,112,152,120, 24), {q}
( 0, 0,176,104, 96, 96), {r}
( 0, 56, 64, 48,136,112), {s}
( 0, 96,248, 96,104, 48), {t}
( 0, 0,200,200,200,120), {u}
( 0, 0,200,200,200,112), {v}
( 0, 0,136,168,168,112), {w}
( 0, 0,216, 96, 48,216), {x}
( 0, 0,200,248, 8,112), {y}
( 0, 0,240, 48,192,248), {z}
( 0, 56, 96,192, 96, 56),(*{*)
( 0, 16, 16, 0, 16, 16), {|}
( 0,224, 48, 24, 48,224),(*}*)
( 0,104,144, 0, 0, 0), {~}
( 0, 32, 80,136,248, 0), {#127}
(112,200,128,200,112,192), {#128}
( 0,200, 0,200,200,120), {#129}
( 24, 32,112,248,128,112), {#130}
( 16, 40, 0,120,196,124), {#131}
(104, 0,112,200,200,120), {#132}
( 48, 8,112,136,136,120), {#133}
( 16, 40, 16,112,200,120), {#134}
( 0,120,192,120, 16, 96), {#135}
(112, 0,112,248,192,112), {#136}
( 80, 0,112,248,128,112), {#137}
( 48, 8,112,248,192,112), {#138}
(104, 0, 48, 48, 48, 48), {#139}
( 48, 72, 0, 48, 48, 48), {#140}
( 96, 16, 0, 48, 48, 48), {#141}
(200, 0,112,200,248,200), {#142}
( 48, 0,112,200,248,200), {#143}
(112,248,192,240,192,248), {#144}
( 0,208, 40,112,160, 88), {#145}
( 0, 56, 80,248,144,152), {#146}
( 32, 80, 0,112,200,112), {#147}
( 80, 0,112,200,200,112), {#148}
( 96, 16, 0,112,200,112), {#149}
( 32, 80, 0,200,200,120), {#150}
( 96, 16, 0,200,200,120), {#151}
( 80, 0,200,248, 8,112), {#152}
( 80, 0,112,200,200,112), {#153}
(200, 0,200,200,200,248), {#154}
( 16,120,128,128,120, 16), {#155}
( 48, 72,224, 64,136,248), {#156}
(216, 32,248, 32,248, 32), {#157}
(192,160,208,184,144,152), {#158}
( 48, 40, 96, 48,160, 96), {#159}
( 48, 64, 0,112,136,120), {#160}
( 48, 64, 0, 32, 32, 32), {#161}
( 48, 64, 0,112,200,112), {#162}
( 48, 64, 0,200,200,120), {#163}
(104,144, 0,176, 72, 72), {#164}
(104,144, 0,200,168,152), {#165}
(112,144,104, 0,248, 0), {#166}
(112,136,112, 0,248, 0), {#167}
( 32, 0, 32, 64,136,112), {#168}
( 0, 0,252,192, 0, 0), {#169}
( 0, 0,252, 12, 0, 0), {#170}
( 72, 80, 32, 64,168, 40), {#171}
( 72, 80, 32, 80,152, 8), {#172}
( 48, 0, 48, 48, 48, 0), {#173}
( 40, 80,160, 80, 40, 0), {#174}
(160, 80, 40, 80,160, 0), {#175}
( 84,168, 84,168, 84,168), {#176}
(252,252,252,252,252,252), {#177}
(168, 84,168, 84,168, 84), {#178}
( 16, 16, 16, 16, 16, 16), {#179}
( 16, 16, 16,240, 16, 16), {#180}
( 16, 16,240, 16,240, 16), {#181}
( 40, 40, 40,232, 40, 40), {#182}
( 0, 0, 0,248, 40, 40), {#183}
( 0, 0,240, 16,240, 16), {#184}
( 40, 40,232, 8,232, 40), {#185}
( 40, 40, 40, 40, 40, 40), {#186}
( 0, 0,248, 8,232, 40), {#187}
( 40, 40,232, 8,248, 0), {#188}
( 40, 40, 40,248, 0, 0), {#189}
( 16, 16,240, 16,240, 0), {#190}
( 0, 0, 0,240, 16, 16), {#191}
( 16, 16, 16, 28, 0, 0), {#192}
( 16, 16, 16,252, 0, 0), {#193}
( 0, 0, 0,252, 16, 16), {#194}
( 16, 16, 16, 28, 16, 16), {#195}
( 0, 0, 0,252, 0, 0), {#196}
( 16, 16, 16,252, 16, 16), {#197}
( 16, 16, 28, 16, 28, 16), {#198}
( 40, 40, 40, 44, 40, 40), {#199}
( 40, 40, 44, 32, 60, 0), {#200}
( 0, 0, 60, 32, 44, 40), {#201}
( 40, 40,236, 0,252, 0), {#202}
( 0, 0,252, 0,236, 40), {#203}
( 40, 40, 44, 32, 44, 40), {#204}
( 0, 0,252, 0,252, 0), {#205}
( 40, 40,236, 0,236, 40), {#206}
( 16, 16,252, 0,252, 0), {#207}
( 40, 40, 40,252, 0, 0), {#208}
( 0, 0,252, 0,252, 16), {#209}
( 0, 0, 0,252, 40, 40), {#210}
( 40, 40, 40, 60, 0, 0), {#211}
( 16, 16, 28, 16, 28, 0), {#212}
( 0, 0, 28, 16, 28, 16), {#213}
( 0, 0, 0, 60, 40, 40), {#214}
( 40, 40, 40,252, 40, 40), {#215}
( 16, 16,252, 16,252, 16), {#216}
( 16, 16, 16,240, 0, 0), {#217}
( 0, 0, 28, 16, 16, 16), {#218}
(252,252,252,252,252,252), {#219}
( 0, 0, 0,252,252,252), {#220}
(192,192,192,192,192,192), {#221}
( 12, 12, 12, 12, 12, 12), {#222}
(252,252,252, 0, 0, 0), {#223}
( 0, 0,104,144,144,104), {#224}
( 0,112,152,176,136,176), {#225}
( 0,248,136,128,128,128), {#226}
( 0, 0,248, 80, 80, 80), {#227}
(248, 72, 32, 64,136,248), {#228}
( 0, 0,120,144,144, 96), {#229}
( 0, 72, 72,120, 64,192), {#230}
( 0, 0,104,176, 32, 32), {#231}
( 0,248, 32, 80, 32,248), {#232}
( 0,112,136,248,136,112), {#233}
( 0,112,136,136, 80,216), {#234}
( 56, 64, 32,112,136,112), {#235}
( 0, 0, 80,168, 80, 0), {#236}
( 0, 8, 80,168, 80,128), {#237}
( 0,120,128,248,128,120), {#238}
( 0, 0,112,136,136,136), {#239}
( 0,248, 0,248, 0,248), {#240}
( 0, 32,112, 32, 0,248), {#241}
( 64, 32, 16, 32, 64,248), {#242}
( 16, 32, 64, 32, 16,248), {#243}
( 16, 40, 32, 32, 32, 32), {#244}
( 32, 32, 32, 32,160, 64), {#245}
( 0, 32, 0,248, 0, 32), {#246}
(104,144, 0,104,144, 0), {#247}
( 96,144, 96, 0, 0, 0), {#248}
( 0, 0, 0, 48, 0, 0), {#249}
( 0, 0, 0, 16, 0, 0), {#250}
( 60, 32, 32,160, 96, 32), {#251}
(176, 72, 72, 0, 0, 0), {#252}
(224, 16, 96,128,240, 0), {#253}
( 0, 0,112,112, 0, 0), {#254}
( 0, 0, 0, 0, 0, 0));{#255}
VAR Steigung:BYTE; {determines, which algorithm will be used }
DY_mal2,DY_m_DX_mal2:INTEGER;
oldMode:byte;
regs:registers;
IsAT:BYTE;
TimeFlag:BYTE;
CycleTime:LONGINT;
CRTAddress, StatusReg : WORD;
{-----------------------------------------------------}
PROCEDURE ShadowTab; ASSEMBLER;
{Pseudo-procedure to store the color lookup table into the code segment}
{DO NOT TRY TO CALL THIS "PROCEDURE"!!! }
{default values correspond to a darkening to 70% of the original brightness}
ASM
DB 254,104,120,124,112,108,114, 24, 20,128,144, 3,136, 5,140, 7
DB 254,254, 17, 17, 18, 19, 20, 20, 21, 8, 23, 24, 24, 25, 26, 7
DB 1, 1,107,108, 5,108,109, 4, 4, 4, 6, 6,116,116,117, 2
DB 2, 2,123,124, 3,124,125, 1,152,155,156,156, 5,156,156,157
DB 160,163,164,164,164,164,164,165,168,171,172,172, 3,172,172,173
DB 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
DB 24, 24, 24, 24, 24, 24, 24, 24,176,177,178,179,180,181,182,183
DB 184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199
DB 200,201,203,204,204,204,205,207,208,209,211,212,212,212,213,215
DB 216,217,219,220,220,220,221,223,246,227,228,228,228,228,228,229
DB 234,235,236,236,236,236,236,237,242,243,244,244,244,244,244,245
DB 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254
DB 254,254,254,254,254,254,254,254, 17, 17, 17, 17, 17, 17, 17, 17
DB 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
DB 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
DB 17, 17, 17, 17, 17, 17, 17, 17,254,254,254,254,254,254,254, 7
END;
PROCEDURE CS_TranslateTab; ASSEMBLER;
{Small pseudo-procedure to store lookup table for the bitmasks into }
{the code segment, too}
ASM
DB 1,2,4,8
END;
PROCEDURE SetShadowTab(brightness:BYTE);
{ in: brightness = wanted brightness for the colors in the shadow area, }
{ in percentage of the brightness of the original colors }
{out: ShadowTab = (best approximation) color table for wanted dimming }
{ Schatten = new brightness (Schatten is a global variable!) }
{rem: default value of ShadowTab is 70% of the original color brightness! }
{ This routine takes some time (about 4 sec on a 8MHz-AT!) }
VAR neue_Tabelle:ColorTable;
p1:POINTER;
BEGIN
IF (brightness<0) OR (brightness>100)
THEN BEGIN
Error:=Err_InvalidPercentage;
exit
END;
p1:=@neue_Tabelle; {trick, as the assembler messes up accesses to the SS-segment}
ASM
MOV CX,256 {outer loop-counter }
LES DI,p1 {ES:DI=^neue_Tabelle[i] }
MOV SI,OFFSET ActualColors {DS:SI=^ActualColors[]}
@outerloop:
LODSB {AL=tempColors[i].red}
MUL brightness {will be addressed via SS! }
MOV DL,100
DIV DL {AL=tempColors[i].red*brightness DIV 100}
MOV BL,AL {BL := AL = new red part }
LODSB {dto., for green }
MUL brightness
MOV DL,100
DIV DL
MOV BH,AL {...into BH}
LODSB {dto., for blue }
MUL brightness
MOV DL,100
DIV DL
MOV DH,AL {...into DH}
{BL/BH/DH = RGB-parts of the color, for which we are seeking an approximation}
PUSH CX
PUSH SI
PUSH DI
PUSH BP
MOV DI,65535 {min. square of error (up to now) }
MOV CX,256 {walk through all 256 default colors}
MOV SI,OFFSET ActualColors {DS:SI=^ActualColors[]}
@searchloop:
MOV AL,BL {compute difference in red part }
SUB AL,[SI]
JL @noNewMin {new color may not be brighter! }
MUL AL {compute square of error}
MOV BP,AX
MOV AL,BH {dto., for green part }
SUB AL,[SI+1]
JL @noNewMin
MUL AL
ADD BP,AX
JC @noNewMin {don't tolerate huge color differences }
MOV AL,DH {dto., for blue part }
SUB AL,[SI+2]
JL @noNewMin
MUL AL
ADD AX,BP
JC @noNewMin
CMP AX,DI {did we find a better approximation?}
JAE @noNewMin {no }
MOV DI,AX {yes, store square of error and color}
MOV DL,CL
OR DI,DI {square of error = 0?}
JZ @ColorDone {yes, we can't find any better solution than that! }
@noNewMin:
ADD SI,3
LOOP @searchloop
CMP DI,65535 {no color found? }
JNE @ColorDone {but yes, nothing to do!}
MOV CX,256 {no, thus search again }
MOV SI,OFFSET ActualColors {DS:SI=^ActualColors[]}
@searchloop2:
LODSB
SUB AL,BL {Diff ≈±2^6 -> square ≈±2^12 -> 3*square<MaxInt }
IMUL AL {so no overflow is possible }
MOV BP,AX
LODSB {dto., for green part }
SUB AL,BH
IMUL AL
ADD BP,AX
LODSB {dto., for blue part }
SUB AL,DH
IMUL AL
ADD AX,BP
CMP AX,DI {did we find a better approximation?}
JAE @noNewMin2 {no }
MOV DI,AX {yes, store square of error and color}
MOV DL,CL
@noNewMin2:
LOOP @searchloop2
@ColorDone: {100h-DL = optimal color found }
POP BP
POP DI {ES:DI=^neue_Tabelle[i] }
POP SI {DS:SI=^ActualColors[i] }
POP CX
MOV AL,DL {store into neue_Tabelle[i] }
NEG AL {AL=100h-DL = best approximation}
STOSB
DEC CX {replacement for "LOOP @outerloop"; next color!}
JCXZ @fertig
JMP @outerloop
@fertig:
END; {of ASM}
MOVE(neue_Tabelle,@ShadowTab^,256); {activate new color table}
Schatten:=brightness
END;
PROCEDURE SetPalette(pal:Palette; update:BOOLEAN);
{ in: pal = pointer to palette to be set }
{ update = TRUE/FALSE for: recompute/don't recompute ShadowTab}
{out: ActualColors = actual color palette }
{rem: palette has been set and evtl., ShadowTab has been recomputed }
BEGIN
IF @pal<>@ActualColors
THEN ActualColors:=pal; {copy palette into ActualColors }
ASM
MOV SI,OFFSET ActualColors {DS:SI=^ActualColors[]}
CLI
mov dx,StatusReg
@WaitNotVSyncLoop:
in al,dx
and al,8
jnz @WaitNotVSyncLoop
@WaitVSyncLoop:
in al,dx
and al,8
jz @WaitVSyncLoop
MOV DX,3C8h
XOR AL,AL
OUT DX,AL
INC DX
MOV CX,256
@L1:
LODSB
OUT DX,AL
LODSB
OUT DX,AL
LODSB
OUT DX,AL
LOOP @L1
STI
END; {of ASM}
IF update THEN SetShadowTab(Schatten)
END;
PROCEDURE GetPalette(VAR pal:Palette); ASSEMBLER;
{ in: pal = pointer to palette memory }
{out: pal = actually set palette }
ASM
CLI
XOR AL,AL
MOV DX,3C7h
OUT DX,AL
LES DI,pal
MOV CX,768
MOV DX,3C9h
@L1:
IN AL,DX
STOSB
LOOP @L1
STI
END;
FUNCTION LoadPalette(name:String; number:BYTE; VAR pal:Palette):WORD;
{ in: name = name of the palette file (type: "*.PAL") to load }
{ number = number for the first color being read in from the file }
{ ActualColors = actually set color palette }
{out: number of colors read from the file (0 = an error occured) }
{ pal = color palette read from the file, evtl. filled up}
{rem: All entries in "pal" which get not overwritten by the file's contents}
{ will become set to the actually set colors of "ActualColors"; the }
{ palette will only become loaded, not actually set!}
LABEL quitloop;
VAR len:LONGINT;
f:File;
i,count:WORD;
TempPal:Palette;
flag:BOOLEAN;
BEGIN
count:=0; {number of palette entries read in til now }
assign(f,name);
{$I-} reset(f,1); {$I+}
if (ioresult<>0)
THEN BEGIN {File doesn't exist or at least not with that path }
Error:=Err_FileIO;
LoadPalette:=0; exit
END;
len:=filesize(f); {determine file length}
if (len mod 3<>0) OR (len>3*256) OR (len<3)
THEN BEGIN
Error:=Err_NoPalette;
goto quitloop;
END;
IF len+number*3>3*256
THEN BEGIN
Error:=Err_PaletteWontFit;
goto quitloop;
END;
TempPal:=ActualColors; {preset temporary palette with actual colors }
{$I-}
blockread(f,TempPal[number],len);
{$I+}
IF (ioresult<>0)
THEN BEGIN
Error:=Err_FileIO;
goto quitloop;
END;
flag:=FALSE;
FOR i:=number TO Pred(number+(len DIV 3))
DO flag:=flag OR (TempPal[i].red>63)
OR (TempPal[i].green>63)
OR (TempPal[i].blue>63);
IF flag
THEN BEGIN
Error:=Err_NoPalette;
goto quitloop;
END;
{everything went alright: return palette}
pal:=TempPal;
count:=len DIV 3;
quitloop: ;
close(f);
LoadPalette:=count
END;
{Now, all the code pieces follow, which can be used to display a sprite; }
{the interface used is the same for all of them: }
{ in: CX = number of bytes, which have to be copied from... }
{ DS:SI = (pointer to source address) to... }
{ ES:BX = (pointer to destination address) }
{ DI = bitplane (0..3) (=X-coordinate AND 3) }
{ The proper bitmask for selecting the correct write plane has al- }
{ ready been set, but not that for the evtl. needed read plane! }
{ The routines can rely upon CX being <>0 }
{rem: Every routine MUST CONSIST OF EXACTLY 16 bytes and BE FULLY RELO- }
{ CATIBLE and MAY NOT CHANGE registers BP,DS and ES!!!!!!!!!!!!!!!!! }
{ Besides that, for distinguishtability, the routines must be pair- }
{ wise disjoint in their first two bytes! }
PROCEDURE Modus0; ASSEMBLER;
{mode 0 considers color 0 being transparent for background data }
ASM
INC CX
STC {change BX such that (together with SI) it can be }
SBB BX,SI {used for accessing the target address}
@L1:
LODSB {fetch sprite byte }
OR AL,AL {is it zero? }
LOOPZ @L1 {yes, ignore it }
JCXZ @L2 {all bytes done? }
MOV ES:[BX+SI],AL {no, store to screen}
JMP @L1 {short} {work on next byte }
@L2:
END;
PROCEDURE Modus1; ASSEMBLER;
{mode 1 writes the data directly to the screen, without further processing }
ASM
MOV DI,BX {set DI so that the string instructions can be used }
XOR AX,AX {set AX:=0 }
SHR CX,1 {number of words to move }
REP MOVSW {move data as one block at once }
ADC CX,AX {does a single byte remain? }
REP MOVSB
MOV AX,AX {4 filling bytes; faster than 4 NOPs}
MOV AX,AX
END;
PROCEDURE Modus2Work; ASSEMBLER;
{Continuation of mode2 - everything, which didn't fit in the reserved 16 }
{bytes is placed here}
ASM
OUT DX,AX {enable read access for correct plane }
PUSH DS {DS still points to sprite data, but must }
{point to background! }
MOV AX,ES {DS:SI := ES:DI (source ptr:=dest. ptr) }
MOV DS,AX
MOV SI,DI
MOV BX,OFFSET ShadowTab {set pointer to color lookup table }
@L4:
LODSB {get background color... }
SEGCS XLAT {...use color lookup table to transform}
STOSB {...and display on actual graphic page}
LOOP @L4
POP DS
END;
PROCEDURE Modus2; ASSEMBLER;
{mode 2 is thought for "shadows" and the like: the sprite's data itself }
{will be ignored; instead, the background data underneath the sprite's }
{position is read in and these color values will be exchanged against }
{those of the color lookup table "ShadowTab" (e.g.: to realize shadows, }
{this table should hold a darker color for each of the original colors) }
ASM
MOV AX,DI {bring bitplane for read access to AX }
MOV DI,BX {put dest. addr. into DI for 8086's string instructions}
MOV AH,AL {bring bitplane to highbyte }
MOV AL,4
MOV DX,3CEh
MOV SI,OFFSET Modus2Work {sort of hack: "CALL Modus2Work" would be coded}
CALL SI {RELATIVE - and thus, jump to ever-neverland! }
END;
PROCEDURE Modus3Work; ASSEMBLER;
{continuation of Modus3 - everything, which didn't fit into 16 bytes }
{bytes is placed here}
ASM
STC
SBB BX,SI {address source and destination with only 1 index register}
MOV DX,BP {save BP-register }
MOV BP,BX
MOV BX,OFFSET ShadowTab {set pointer to color lookup table }
@L1:
LODSB {get sprite data... }
OR AL,AL { (ignore color 0 as "transparent") }
LOOPZ @L1
JCXZ @L2
MOV AL,ES:[BP+SI] {get background color... }
SEGCS XLAT {...use color lookup table to transform}
MOV ES:[BP+SI],AL {...and display on actual graphic page}
JMP @L1
@L2:
MOV BP,DX {restore old contents of BP register }
END;
PROCEDURE Modus3; ASSEMBLER;
{Modus3 is thought for "shadows", too: in this mode, all sprite }
{pixels with color <>0 will be processed: the background color, which is }
{underneath these pixels will be replaced by the corresponding color entry }
{from the table "ShadowTab" }
{In other words: this mode is equivalent to Modus2, with the exception }
{that sprite color 0 is treated as being transparent for shadows }
{too! }
ASM
MOV DX,3CEh {prepare access to read plane: }
MOV AX,DI
MOV AH,AL {load read plane into AH }
MOV AL,4
OUT DX,AX {enable read access for correct plane }
INC CX {inc. number of bytes by 1 (-> LODSB!) }
MOV AX,OFFSET Modus3Work {trick to make code relocatible! }
CALL AX
END;
PROCEDURE Adressen; ASSEMBLER;
{table with the starting addresses of the 3 routines in the code segment}
ASM
DW OFFSET Modus0
DW OFFSET Modus1
DW OFFSET Modus2
DW OFFSET Modus3
END;
PROCEDURE GADR; ASSEMBLER;
{table with graphic rows starting addresses (offset part)}
ASM
DW $0000,$0050,$00A0,$00F0,$0140,$0190,$01E0,$0230
DW $0280,$02D0,$0320,$0370,$03C0,$0410,$0460,$04B0
DW $0500,$0550,$05A0,$05F0,$0640,$0690,$06E0,$0730
DW $0780,$07D0,$0820,$0870,$08C0,$0910,$0960,$09B0
DW $0A00,$0A50,$0AA0,$0AF0,$0B40,$0B90,$0BE0,$0C30
DW $0C80,$0CD0,$0D20,$0D70,$0DC0,$0E10,$0E60,$0EB0
DW $0F00,$0F50,$0FA0,$0FF0,$1040,$1090,$10E0,$1130
DW $1180,$11D0,$1220,$1270,$12C0,$1310,$1360,$13B0
DW $1400,$1450,$14A0,$14F0,$1540,$1590,$15E0,$1630
DW $1680,$16D0,$1720,$1770,$17C0,$1810,$1860,$18B0
DW $1900,$1950,$19A0,$19F0,$1A40,$1A90,$1AE0,$1B30
DW $1B80,$1BD0,$1C20,$1C70,$1CC0,$1D10,$1D60,$1DB0
DW $1E00,$1E50,$1EA0,$1EF0,$1F40,$1F90,$1FE0,$2030
DW $2080,$20D0,$2120,$2170,$21C0,$2210,$2260,$22B0
DW $2300,$2350,$23A0,$23F0,$2440,$2490,$24E0,$2530
DW $2580,$25D0,$2620,$2670,$26C0,$2710,$2760,$27B0
DW $2800,$2850,$28A0,$28F0,$2940,$2990,$29E0,$2A30
DW $2A80,$2AD0,$2B20,$2B70,$2BC0,$2C10,$2C60,$2CB0
DW $2D00,$2D50,$2DA0,$2DF0,$2E40,$2E90,$2EE0,$2F30
DW $2F80,$2FD0,$3020,$3070,$30C0,$3110,$3160,$31B0
DW $3200,$3250,$32A0,$32F0,$3340,$3390,$33E0,$3430
DW $3480,$34D0,$3520,$3570,$35C0,$3610,$3660,$36B0
DW $3700,$3750,$37A0,$37F0,$3840,$3890,$38E0,$3930
DW $3980,$39D0,$3A20,$3A70,$3AC0,$3B10,$3B60,$3BB0
DW $3C00,$3C50,$3CA0,$3CF0,$3D40,$3D90,$3DE0,$3E30
END;
FUNCTION AT:BOOLEAN;
{ in: - }
{out: TRUE/FALSE, if the machine is (at least) an AT }
BEGIN
AT:=MEM[$F000:$FFFE]=$FC
END;
PROCEDURE SetCycleTime(milliseconds:WORD);
{ in: min. time for one animation cycle in milliseconds }
{out: CycleTime := that value in microseconds }
{ TimeFlag := $80}
{rem: Because of TimeFlag:=$80, the timing mechanism won't work }
{ for the very first animation cycle, yet! }
{ If you don't use the timing mechanism (by supplying a value}
{ of 0 milliseconds), this will result in IsAT:=$80, that is }
{ the routine will fake "computer is a PC". Else IsAT=0 }
BEGIN
TimeFlag:=$80;
CycleTime:=LONGINT(milliseconds)*LONGINT(1000);
IF (milliseconds<>0) AND AT
THEN IsAT:=0 {yes, time control mechanism shall be used }
ELSE IsAT:=$80 {no, none possible or not wanted }
END;
PROCEDURE SetSpriteCycle(nr,len:WORD);
{ in: nr = spriteloadnumber of the first sprite in the cycle }
{ len = length of your sprite cycle }
{out: NextSprite[nr] through NextSprite[nr+len-1] have been set }
{ set such that they build a "ring", that is: together }
{ they make up a sprite cycle}
{rem: If the sprite cycle shall consist of (physical) sprites }
{ whose load numbers aren't consecutive, then you }
{ have to make the appropriate entries into NextSprite[] }
{ yourself manually }
{ This routine uses spriteLOADnumbers! }
VAR i:WORD;
BEGIN
IF (nr<1) OR (nr+len-1>LoadMAX)
THEN Error:=Err_InvalidSpriteLoadNumber
ELSE BEGIN
FOR i:=nr TO nr+len-2 DO NextSprite[i]:=SUCC(i);
NextSprite[PRED(nr+len)]:=nr {last sprite points to first one}
END;
END;
FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:BYTE):POINTER;
{ in: (x1,y1) = upper left corner of the area which shall be stored }
{ (x2,y2) = according lower right corner (in virtual coordinates!) }
{ pa = graphic page from which the image should be taken (0..2) }
{ StartVirtualX,StartVirtualY = upper left image corner }
{out: pointer to heap address where the copied screen area is stored }
{ left_cut= evtl. needed left cut off of the image (this determines, }
{ how many pixels at the left edge of the image lie outside }
{ of the screen) }
{ right_cut,top_cut,bottom_cut = dto., for the other boundaries }
{ was_cut = TRUE/FALSE, if it was necessary/not necessary to clip the }
{ fetched image }
{rem: The memory needed will be reserved by the routine automatically }
{ If that is impossible (or the image is completely offscreen), the }
{ routine will return NIL! }
{ Only if "was_cut" is set to TRUE, the (global) "..._cut" variables }
{ will be set to something other then 0, that is: if the window lies }
{ _completely_ offscreen (this means: returned ptr=NIL), then the }
{ routine still returns "was_cut"=FALSE!}
VAR len,breite,hoehe,StartAdr,actualAdr,SegmAdr:WORD;
p:POINTER;
BEGIN
was_cut:=FALSE; left_cut:=0; right_cut:=0; top_cut:=0; bottom_cut:=0;
dec(x1,StartVirtualX); {compute screen coordinates }
dec(y1,StartVirtualY);
IF (x1>XMAX) or (y1>YMAX) or (x2<0) or (y2<0) or (x1>x2) or (y1>y2)
THEN BEGIN {window is offscreen }
GetImage:=NIL;
exit
END;
{cut clipping according to visible screen:}
IF x1<0 THEN BEGIN left_cut :=-x1; x1:=0; was_cut:=TRUE END;
IF y1<0 THEN BEGIN top_cut:=-y1; y1:=0; was_cut:=TRUE END;
IF x2>XMAX THEN BEGIN right_cut :=x2-XMAX; x2:=XMAX; was_cut:=TRUE END;
IF y2>YMAX THEN BEGIN bottom_cut:=y2-YMAX; y2:=YMAX; was_cut:=TRUE END;
breite:=SUCC(x2-x1); hoehe:=SUCC(y2-y1);
len:=breite*hoehe+2*2; {1 pixel=1 byte; add 2 words for width & height }
IF len>MaxAvail
THEN BEGIN
Error:=Err_NotEnoughMemory;
GetImage:=NIL;
exit
END;
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE) {page number must be 0..2 }
THEN BEGIN
Error:=Err_InvalidPageNumber;
GetImage:=NIL;
exit
END
ELSE SegmAdr:=Segment_Adr[pa];
GetMem(p,len); {get memory from the heap }
ASM
CLD
LES DI,p {ES:DI = pointer to the acquired memory }
MOV AX,breite
STOSW {store width first... }
MOV AX,hoehe
STOSW {...then store height, followed by the data }
MOV BX,AX {BX:=height (to be used later) }
MOV SI,y1
SHL SI,1
MOV SI,CS:[OFFSET gadr + SI] {SI:=y1*LINESIZE}
MOV AX,x1
MOV DL,AL
SHR AX,1
SHR AX,1
ADD SI,AX {SI:=offset part of the start address}
MOV StartAdr,SI
MOV actualAdr,SI
AND DL,3
MOV AH,DL
MOV AL,4
MOV DX,3CEh
OUT DX,AX {select start plane }
MOV DS,SegmAdr
{DS:SI = pointer to first byte to store; ES:DI = its target address }
{AH = startplane, AL = 4, BX = number of rows to process }
MOV DX,breite
ADD DX,3
SHR DX,1
SHR DX,1 {DX = number of bytes to store for each row}
@L1:
MOV CX,DX {store data of one row }
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,0
REP MOVSB
MOV SI,actualAdr {increment source pointer by 1 graphic row}
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX {decrease row counter }
JNE @L1
INC AH {select next plane }
CMP AH,4
JNE @nowrap1 {wrapping the bitplane means: start address }
MOV AH,0 {needs mending: increment address by 1! }
INC StartAdr
@nowrap1:
MOV DX,3CEh
OUT DX,AX
MOV BX,hoehe
MOV DX,breite
INC DX
INC DX
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L2:
MOV CX,DX
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,0
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L2
INC AH
CMP AH,4
JNE @nowrap2
MOV AH,0
INC StartAdr
@nowrap2:
MOV DX,3CEh
OUT DX,AX
MOV BX,hoehe
MOV DX,breite
INC DX
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L3:
MOV CX,DX
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,0
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L3
INC AH
CMP AH,4
JNE @nowrap3
MOV AH,0
INC StartAdr
@nowrap3:
MOV DX,3CEh
OUT DX,AX
MOV BX,hoehe
MOV DX,breite
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L4:
MOV CX,DX
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,0
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L4
MOV AX,SEG @DATA
MOV DS,AX
END;
GetImage:=p
END;
PROCEDURE PutImage(x,y:INTEGER; p:POINTER; pa:BYTE);
{ in: (x,y) = upper left corner of destination (virtual coordinates) }
{ p = pointer to the cutting (returned by GetImage) }
{ pa = graphic page to which the cutting shall be pasted to }
{ StartVirtualX,StartVirtualY = upper left image corner }
{out: - }
{rem: The cutting has been properly clipped before being displayed }
{ If you supply NIL as pointer, the routine will display nothing }
{ That is useful when you are going to use the routine directly on }
{ the results supplied by GetImage! }
VAR breite,hoehe,SegmAdr,actualAdr,StartAdr,breite1,breite2,breite3,breite4,
licut_div4,topcut,pl_adr1,pl_adr2,pl_adr3,pl_adr4:WORD;
licutoff,temp:INTEGER;
BEGIN
IF p=NIL THEN exit;
dec(x,StartVirtualX); {compute screen coordinates }
dec(y,StartVirtualY);
IF (x>XMAX) or (y>YMAX) THEN exit;
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber;
exit
END
ELSE SegmAdr:=Segment_Adr[pa];
breite:=MEMW[SEG(p^):OFS(p^)];
hoehe :=MEMW[SEG(p^):OFS(p^)+2];
IF (x+breite<=0) or (y+hoehe<=0) THEN exit;
IF x<0 THEN BEGIN licutoff:=-x; x:=0 END
ELSE licutoff:=0;
IF y<0 THEN BEGIN
topcut:=-y;
y:=0
END
ELSE topcut:=0;
breite1:=(breite + 3) shr 2; {Width of a row for the first, second, }
breite2:=(breite + 2) shr 2; {third and fourth bitplane, respectively}
breite3:=(breite + 1) shr 2;
breite4:=(breite + 0) shr 2;
{Compute starting addresses of the 4 bitplanes; take into account evtl. }
{left cutoff (+4 bytes to jump over "breite" (width) and "hoehe" (height) }
licut_div4:=licutoff shr 2;
pl_adr1:=4 +licut_div4 +topcut*breite1;
pl_adr2:=4 +licut_div4 +topcut*breite2 +hoehe*breite1;
pl_adr3:=4 +licut_div4 +topcut*breite3 +hoehe*(breite1+breite2);
pl_adr4:=4 +licut_div4 +topcut*breite4 +hoehe*(breite1+breite2+breite3);
{licutoff mod 4 determines the order in which the points must be read }
{of the heap: 0 = plane order (1,2,3,4); 1 = plane order (2,3,4,1); }
{2=(3,4,1,2); 3=(4,1,2,3); note that the widths of the bitplane tables }
{are (and remain) linked to these; therefore, they will be swapped too }
{to accomplish that! }
ASM
CLD
MOV AX,licutoff
AND AL,3
OR AL,AL
JE @no_exchange
CMP AL,1
JNE @L10
MOV AX,pl_adr2 {displacement of 1 bit: }
MOV BX,pl_adr3 {AX=Plane2,BX=Plane3,CX=Plane4,DX=Plane1}
MOV CX,pl_adr4
MOV DX,pl_adr1 {wrap-around, thus: increment address by 1, which }
INC DX {corresponds to an ajustment of 4 points }
MOV pl_adr1,AX {(e.g.: pixels (1,5,9,...),(2,6,10,...),(3,7,11,...)}
MOV pl_adr2,BX {and (0,4,8,...); the last bitplane needs a cor- }
MOV pl_adr3,CX {rection of +1 byte: this results in (4,8,12,...) }
MOV pl_adr4,DX {(read planes top-down, in alternating order!) }
MOV AX,breite2 {Now the plane widths: }
MOV BX,breite3 {AX=Plane2,BX=Plane3,CX=Plane4,DX=Plane1}
MOV CX,breite4
MOV DX,breite1
JMP @store
@L10:
CMP AL,2
JNE @L20
MOV AX,pl_adr3 {displacement of 2 bit: }
MOV BX,pl_adr4 {AX=Plane3,BX=Plane4,CX=Plane1,DX=Plane2}
MOV CX,pl_adr1
INC CX
MOV DX,pl_adr2
INC DX
MOV pl_adr1,AX
MOV pl_adr2,BX
MOV pl_adr3,CX
MOV pl_adr4,DX
MOV AX,breite3 {dto. for plane widths: }
MOV BX,breite4 {AX=Plane3,BX=Plane4,CX=Plane1,DX=Plane2}
MOV CX,breite1
MOV DX,breite2
JMP @store
@L20:
MOV AX,pl_adr4 {displacement of 3 bit: }
MOV BX,pl_adr1 {AX=Plane4,BX=Plane1,CX=Plane2,DX=Plane3}
INC BX
MOV CX,pl_adr2
INC CX
MOV DX,pl_adr3
INC DX
MOV pl_adr1,AX
MOV pl_adr2,BX
MOV pl_adr3,CX
MOV pl_adr4,DX
MOV AX,breite4 {dto. for plane widths: }
MOV BX,breite1 {AX=Plane4,BX=Plane1,CX=Plane2,DX=Plane3}
MOV CX,breite2
MOV DX,breite3
@store:
MOV breite1,AX
MOV breite2,BX
MOV breite3,CX
MOV breite4,DX
@no_exchange: {precondition here: (pl_adr?,breite?) contain the }
{source bitplanes/-widths in the correct order }
MOV AX,topcut
SUB hoehe,AX {evtl. adjust height for upper cutoff }
MOV AX,licutoff
SUB breite,AX {dto. for width and left cutoff }
MOV AX,x {if image would spread over the right screen }
ADD AX,breite {boundary: compute right cutoff }
SUB AX,XMAX+1
JLE @no_recutoff
SUB breite,AX {cut off AX points at the right}
@no_recutoff:
MOV AX,y {exactly the same for the lower screen border}
ADD AX,hoehe
SUB AX,YMAX+1
JLE @no_bocutoff
SUB hoehe,AX {cut off AX rows at the bottom}
@no_bocutoff:
LDS SI,p
ADD pl_adr2,SI {add pointer's offset part to the plane address }
ADD pl_adr3,SI
ADD pl_adr4,SI
ADD SI,pl_adr1 {width,height and parts above the screen }
MOV ES,SegmAdr
MOV DI,y
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI] {DI:=y*LINESIZE}
MOV AX,x
MOV BL,AL
SHR AX,1
SHR AX,1
ADD DI,AX {DI:=y*LINESIZE +(x DIV 4)}
MOV StartAdr,DI
MOV actualAdr,DI
AND BX,3 {startplane:=x mod 3}
MOV AH,CS:[OFFSET CS_TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
OUT DX,AX {use it as write plane }
MOV DX,hoehe
MOV DI,actualAdr
{DS:SI = pointer to data, ES:DI = dest. address on screen for them }
{AH = bitmask for access, AL = 2 }
MOV BX,breite
ADD BX,3
SHR BX,1
SHR BX,1
mov cx,bx
@L1:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,0
REP MOVSB
pop si
mov cx,bx
add si,breite1
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L1
SHL AH,1 {select next bitplane; if wrap-around occurs }
CMP AH,16 {from bitplane 3 to bitplane 0, then the start- }
JNE @nowrap1 {ing address must be incremented by 1 byte }
MOV AH,1
INC StartAdr
@nowrap1:
MOV DX,3C4h
OUT DX,AX
MOV SI,pl_adr2
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
INC BX
INC BX
SHR BX,1
SHR BX,1
mov cx,bx
@L2:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,0
REP MOVSB
pop si
mov cx,bx
add si,breite2
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L2
SHL AH,1
CMP AH,16
JNE @nowrap2
MOV AH,1
INC StartAdr
@nowrap2:
MOV DX,3C4h
OUT DX,AX
MOV SI,pl_adr3
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
INC BX
SHR BX,1
SHR BX,1
mov cx,bx
@L3:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,0
REP MOVSB
pop si
mov cx,bx
add si,breite3
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L3
SHL AH,1
CMP AH,16
JNE @nowrap3
MOV AH,1
INC StartAdr
@nowrap3:
MOV DX,3C4h
OUT DX,AX
MOV SI,pl_adr4
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
SHR BX,1
SHR BX,1
mov cx,bx
@L4:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,0
REP MOVSB
pop si
mov cx,bx
add si,breite4
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L4
MOV AX,SEG @DATA
MOV DS,AX
END;
END;
PROCEDURE FreeImageMem(p:POINTER);
{ in: p = pointer to image memory, allocated by GetImage()}
{out: - }
{rem: the heap memory allocated for the image has been released }
BEGIN
IF p<>NIL THEN FreeMem(p,MEMW[Seg(p^):Ofs(p^)]*MEMW[Seg(p^):Ofs(p^)+2] + 2*2)
END;
PROCEDURE Screen(pa:BYTE);
{ in: pa = graphic page to be shown (0..3) }
{out: - }
{rem: The display has been switched to graphic page pa }
{ The routine does NOT synchronize on any retrace-signal }
{ Sensible page values are only 0 or 1 here, but the routine }
{ doesn't make any checks!}
BEGIN
ASM
MOV DX,CRTAddress {CRT-Controller}
MOV AL,$0D {LB-startaddress-register}
CLI {May not be interrupted! }
OUT DX,AL
INC DX
{realize "AX:=Offset_Adr[pa]": }
MOV BL,pa
MOV SI,BX
AND SI,3 {page value *2 (word-sized entries!)}
SHL SI,1 {add start address of array to that }
ADD SI,OFFSET Offset_Adr-StartIndex*2 {evtl. correct displacement }
LODSW {and fetch value}
OUT DX,AL {set LB of new starting address }
DEC DX
MOV AL,$0C
OUT DX,AL
INC DX
MOV AL,AH {set HB of new starting address }
OUT DX,AL
STI
END;
END;
PROCEDURE InitGraph;
{ in: PAGE = actual graphic page }
{out: - }
{rem: switches the VGA-card into 320x200x256x4-mode; ATTENTION! }
{ This mode is different from mode $13 of the VGA-BIOS!!! }
{ The display will be switched to graphic page 1-PAGE }
{ The default colors of mode $13 will be set! }
BEGIN
ASM
MOV AX,0013h {use BIOS to set graphic mode $13 (320x200x256) }
INT 10h
MOV DX,03C4h {select memory-mode-register at sequencer port }
MOV AL,04
OUT DX,AL
INC DX {read in data via the according data register }
IN AL,DX
AND AL,0F7h {bit 3:=0 -> don't chain planes }
OR AL,04 {bit 2:=1 -> no odd/even-scheme }
OUT DX,AL {activate new settings }
MOV DX,03C4h {s.a.: address sequencer reg. 2 (=map-mask),... }
MOV AL,02
OUT DX,AL
INC DX
MOV AL,0Fh {...and allow access to all 4 bit maps }
OUT DX,AL
MOV AX,0A000h {starting in segment A000h, set 8000h logical }
MOV ES,AX {words = 4*8000h physical words (because of 4 }
SUB DI,DI {bitplanes) to 0 }
MOV AX,DI
MOV CX,8000h
CLD
REP STOSW
MOV DX,CRTAddress {address the underline-location-register at }
MOV AL,14h {the CRT-controller port, read out the according }
OUT DX,AL {data register: }
INC DX
IN AL,DX
AND AL,0BFh {bit 6:=0 -> no double word addressing scheme in }
OUT DX,AL {video ram }
DEC DX
MOV AL,17h {select mode control register }
OUT DX,AL
INC DX
IN AL,DX
OR AL,40h {bit 6:=1 -> address memory as a linear bit array }
OUT DX,AL
END;
Screen(1-PAGE); {ALWAYS is the non-actual graphic page the visible one!}
SetPalette(DefaultColors,FALSE) {set default palette, just to be sure! }
END;
PROCEDURE Line(x1,y1,x2,y2:INTEGER; pa:BYTE);
{ in: x1,y1,x2,y2 = coordinates of two points, }
{ Color = color (0..255) }
{ StartVirtualX,StartVirtualY = upper left image corner }
{ pa = graphic page to be drawn upon (0..2) }
{out: - }
{rem: A line has been drawn between the VIRTUAL points (x1,y1) and (x2,y2) }
{ using the color COLOR; the routine will take care of transforming }
{ the coordinates to absolute screen coordinates and evtl. necessary }
{ clipping actions. }
{ The line will NOT automatically be taken over into the background }
{ image, that is: it will be visible only for one animation cycle (if }
{ it shall stay permanent, you have to draw it into the background!) }
{ (For that reason, you should call this routine AFTER calling ANIMATE }
{ because otherwise, the drawn line will vanish at once!) }
CONST CodeLinks =$7; {%0111}
CodeRechts=$B; {%1011}
CodeOben =$D; {%1101}
CodeUnten =$E; {%1110}
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN Error:=Err_InvalidPageNumber
ELSE
{first clip line to visible window; use Sutherland-Cohen-algorithm: }
{use 4 bit-codes for: left|right|top|bottom }
ASM
CLD
MOV CL,$F {start with %1111 }
MOV AX,x2
SUB AX,StartVirtualX {transform x2 into absolue coordinates}
MOV x2,AX
OR AX,AX {x2<0 ?}
JL @GC1Punkt2 {yes, don't change flag for "point is left of window"}
AND CL,CodeLinks {no, reset flag }
@GC1Punkt2:
CMP AX,XMAX {x2>XMAX ?}
JG @GC2Punkt2 {yes, don't change flag for "point is right of window"}
AND CL,CodeRechts {no, reset flag }
@GC2Punkt2:
MOV AX,y2
SUB AX,StartVirtualY {transform y2 into absolue coordinates}
MOV y2,AX
OR AX,AX {y2<0 ?}
JL @GC3Punkt2 {yes, don't change flag for "point is above window"}
AND CL,CodeOben {no, reset flag }
@GC3Punkt2:
CMP AX,YMAX {y2>YMAX ?}
JG @GC4Punkt2 {yes, don't change flag for "point is below window"}
AND CL,CodeUnten
@GC4Punkt2: {CL holds the area code for point 2 }
MOV AX,x1
SUB AX,StartVirtualX {transform x1 into absolue coordinates}
MOV x1,AX
MOV AX,y1
SUB AX,StartVirtualY {transform y1 into absolue coordinates}
MOV y1,AX
@Punkt1:
MOV CH,$F {start with %1111 }
MOV AX,x1
OR AX,AX {x1<0 ?}
JL @GC1Punkt1 {yes, don't change flag for "point is left of window"}
AND CH,CodeLinks {no, reset flag }
@GC1Punkt1:
CMP AX,XMAX {x1>XMAX ?}
JG @GC2Punkt1 {yes, don't change flag for "point is right of window"}
AND CH,CodeRechts {no, reset flag }
@GC2Punkt1:
MOV AX,y1
OR AX,AX {y1<0 ?}
JL @GC3Punkt1 {yes, don't change flag for "point is above window"}
AND CH,CodeOben {no, reset flag }
@GC3Punkt1:
CMP AX,YMAX {y1>YMAX ?}
JG @GC4Punkt1 {yes, don't change flag for "point is below window"}
AND CH,CodeUnten
@GC4Punkt1: {CH holds the area code for point 1 }
{CL holds the area code for point 2, CH the one for point 1 }
MOV AX,CX
AND AL,AH {Code1 AND Code2 <>0 ?}
JNZ @LineReady {yes, line is completely outside the window}
MOV AX,CX
OR AL,AH {Code1 OR Code2 =0 ?}
JZ @DrawLine {yes, line is completely inside the window}
{Now do the clipping itself: }
MOV AX,CX
OR AH,AH {Code1 =0 ?}
JNZ @CL3 {no, everything ok}
MOV AX,x1 {yes, swap points! }
XCHG AX,x2
MOV x1,AX
MOV AX,y1
XCHG AX,y2
MOV y1,AX
XCHG CL,CH
@CL3:
MOV AL,CH {AL:=Code1}
MOV BX,x2
SUB BX,x1 {BX:=x2-x1}
MOV SI,y2
SUB SI,y1 {SI:=y2-y1}
TEST AL,NOT CodeLinks {point1 left of window? }
JZ @CL4 {no }
{yes, compute new coordinates: y1:=y1+(y2-y1)/(x2-x1)*(WindowX1-X1) }
{and x1:=WindowX1 (here, WindowX1 = 0) }
XOR AX,AX
XCHG AX,x1 {x1:=0}
NEG AX {AX:=-x1old}
IMUL SI
IDIV BX
ADD y1,AX
JMP @Punkt1
@CL4:
TEST AL,NOT CodeRechts {point1 right of window? }
JZ @CL5 {no }
{yes, compute y1:=y1+(y2-y1)/(x2-x1)*(WindowX2-X1), x1:=WindowX2 }
{ (here, WindowX2=XMAX) }
MOV AX,XMAX
SUB AX,x1
IMUL SI
IDIV BX
ADD y1,AX
MOV x1,XMAX
JMP @Punkt1
@CL5:
TEST AL,NOT CodeOben {point1 above window? }
JZ @CL6 {no }
{yes, compute x1:=x1+(x2-x1)/(y2-y1)*(WindowY1-y1), y1:=WindowY1 }
{ (here, WindowY1=0) }
XOR AX,AX
XCHG AX,y1
NEG AX
IMUL BX
IDIV SI
ADD x1,AX
JMP @Punkt1
@CL6:
TEST AL,NOT CodeUnten {point below window? }
JZ @Punkt1 {no }
{yes, compute x1:=x1+(x2-x1)/(y2-y1)*(WindowY2-y1), y1:=WindowY2 }
{ (here, WindowY2=YMAX) }
MOV AX,YMAX
SUB AX,y1
IMUL BX
IDIV SI
ADD x1,AX
MOV y1,YMAX
JMP @Punkt1
{precondition here: both points have been clipped to the visible window;}
{if the line is completely offscreen, the program jumped directly to }
{@LineReady, instead! }
@DrawLine:
PUSH BP
MOV Steigung,0 {reset Flag }
MOV CX,x2
SUB CX,x1 {point1 right of point2 ? }
JGE @posDX {no }
NEG CX {yes, swap points }
MOV AX,x1
XCHG AX,x2
MOV x1,AX
MOV AX,y1
XCHG AX,y2
MOV y1,AX
@posDX:
MOV DI,y1
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI] {DI:=y1*LINESIZE}
MOV AX,x1
MOV BL,AL
SHR AX,1
SHR AX,1
ADD DI,AX {DI:=y1*LINESIZE+(x1 DIV 4) }
AND BX,3 {BX:=(x1 AND 4) }
MOV DH,[OFFSET TranslateTab + BX] {get mask for VRAM-access }
MOV DL,2
MOV BL,pa {BH=0 -> BX=drawing page}
SHL BX,1
ADD BX,OFFSET Segment_Adr -StartIndex*2
MOV ES,[BX]
{ES:DI=pointer to graphic address of point1, DX=access mask for it }
MOV SI,LINESIZE
MOV BX,y2
SUB BX,y1 {point1 below point2 ? }
JG @posDY {no }
NEG BX {yes, negate deltaY and row-increment }
NEG SI
@posDY:
CMP BX,CX {deltaY>deltaX ?}
JLE @flach {no: small slope, <=1 }
XCHG BX,CX {yes, swap deltas and set flag }
MOV Steigung,1
{compute Bresenham-parameters: 2*DY, 2*DY-DX, 2*(DY-DX) }
@flach:
SHL BX,1
MOV DY_mal2,BX
SUB BX,CX
MOV BP,BX {BP:=2*DY-DX}
SUB BX,CX
MOV DY_m_DX_mal2,BX
INC CX {CX:=number of pixels}
MOV BL,Color
MOV BH,1
CMP Steigung,0 {steep line? }
JNZ @high1 {yes}
@low1: {no }
MOV AX,3C4h
XCHG AX,DX
OUT DX,AX {select correct bitplane }
MOV DX,AX {save mask to DX again }
MOV AL,BL {get color of point }
STOSB {draw point }
SHL DH,1 {compute mask for next point }
CMP DH,16 {still addressable with the same address? }
JE @nextbyte1 {no, address had to be incremented by 1 }
DEC DI {yes, make increase of DI undone }
@low1b:
OR BP,BP
JGE @low2
ADD BP,DY_mal2
LOOP @low1
JMP @raus
@nextbyte1:
MOV DH,BH {restore mask to 1 }
JMP @low1b {rest as above }
@low2:
ADD BP,DY_m_DX_mal2
ADD DI,SI
LOOP @low1
JMP @raus
@high1:
MOV AX,3C4h
XCHG AX,DX
OUT DX,AX
MOV DX,AX
MOV AL,BL
@high1b:
OR BP,BP
JGE @high2
ADD BP,DY_mal2
MOV ES:[DI],AL
ADD DI,SI
LOOP @high1b
JMP @raus
@high2:
ADD BP,DY_m_DX_mal2
SHL DH,1
CMP DH,16
JE @nextbyte2
MOV ES:[DI],AL
ADD DI,SI
LOOP @high1
JMP @raus
@nextbyte2:
MOV DH,BH
STOSB
ADD DI,SI
LOOP @high1
@raus:
POP BP
@LineReady:
END;
END;
PROCEDURE BackgroundLine(x1,y1,x2,y2:INTEGER);
{ in: x1,y1,x2,y2 = coordinates of two points, }
{ Color = color (0..255) }
{ StartVirtualX,StartVirtualY = upper left image corner }
{out: - }
{rem: A line has been drawn between the VIRTUAL points (x1,y1) and (x2,y2) }
{ (using the color COLOR) into the background page; the routine it- }
{ self takes care of coordinate transformations and evtl. necessary }
{ clipping actions. }
{ The line will NOT be visible until the next animation cycle takes }
{ place (but then, it stays permanent)! (For that reason, you normally}
{ will call this routine BEFORE calling ANIMATE, because this way, all }
{ changes will become visible (via ANIMATE) at once) }
{ Because BACKGNDADR is used as background page, calling this rou- }
{ tine only makes sense when using background mode STATIC! }
BEGIN
Line(x1,y1,x2,y2,BACKGNDPAGE)
END;
FUNCTION GetPixel(x,y:INTEGER):BYTE; ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be read }
{ PAGEADR= graphic page(segment) to be read from }
{ StartVirtualX, StartVirtualY = upper left image corner }
{out: color of the point}
{rem: If the pixel lies outside the visible window, the routine }
{ will return "0" as the result }
{ Attention! As PAGEADR always specifies the NOT visible gra- }
{ phic page, this routine will read from there, too! }
ASM
XOR AL,AL {preset AL with 0 }
MOV DI,y
SUB DI,StartVirtualY {transform y into absolue coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BL,3 {BL = X MOD 4 = plane to read from}
MOV AL,4
MOV AH,BL
MOV DX,3CEh
MOV ES,PAGEADR
CLI
OUT DX,AX
MOV AL,ES:[DI]
STI
@offscrn:
END;
FUNCTION BackgroundGetPixel(x,y:INTEGER):BYTE; ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be read }
{ StartVirtualX, StartVirtualY = upper left image corner }
{out: color of the point in the background page}
{rem: If the pixel lies outside the visible window, the routine }
{ will return "0" as the result }
{ Because BACKGNDADR is used as background page, calling }
{ this routine only makes sense when using mode STATIC! }
ASM
XOR AL,AL {preset AL with 0 }
MOV DI,y
SUB DI,StartVirtualY {transform y into absolue coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BL,3 {BL = X MOD 4 = plane to read from}
MOV AL,4
MOV AH,BL
MOV DX,3CEh
MOV ES,BACKGNDADR
CLI
OUT DX,AX
MOV AL,ES:[DI]
STI
@offscrn:
END;
FUNCTION PageGetPixel(x,y:INTEGER; pa:BYTE):BYTE; ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be read }
{ pa = graphic page (0..3), from which the point shall be }
{ read out }
{ StartVirtualX, StartVirtualY = upper left image corner }
{out: color of the point in the background page}
{rem: If the pixel lies outside the visible window, the routine }
{ will return "0" as the result }
{ If you want to read from the actually VISIBLE page, then }
{ then you must call the routine with pa=1-PAGE! }
{ Sensible values for "pa" are either 0 or 1 (and evtl. BACK- }
{ GNDPAGE, if you are using background mode STATIC), however, }
{ the routine doesn't check that! }
ASM
XOR AL,AL {preset AL with 0 }
MOV DI,y
SUB DI,StartVirtualY {transform y into absolue coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BX,3 {BL = X MOD 4 = plane to read from; BH = 0}
MOV AL,4
MOV AH,BL
MOV BL,pa {BH=0 -> BX = graphic page}
AND BX,3 {only pages 0..3}
SHL BX,1
ADD BX,OFFSET Segment_Adr-StartIndex*2
MOV ES,[BX]
CLI
MOV DX,3CEh
OUT DX,AX
MOV AL,ES:[DI]
STI
@offscrn:
END;
PROCEDURE PutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be written }
{ color = color value for the pixel to be drawn }
{ 1-PAGE = graphic page to be drawn upon }
{ StartVirtualX, StartVirtualY = upper left image corner }
{out: - }
{rem: The point (x,y) has been transformed to absolute screen coordinates }
{ and has been drawn (if it lies within the visible window) }
{ The pixel will NOT automatically be overtaken into the background }
{ image, that is: it will be visible only for one animation cycle! }
{ (For that reason, you should call this routine AFTER calling ANIMATE }
{ because otherwise, the drawn point will vanish at once!) }
ASM
MOV DI,y
SUB DI,StartVirtualY {transform y into absolue coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BX,3
MOV AH,[OFFSET TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
MOV BX,1 {ES:=Segment_Adr[1-PAGE], because 1-PAGE=visible page}
SUB BX,PAGE
SHL BX,1
ADD BX,OFFSET Segment_Adr-StartIndex*2
MOV ES,[BX]
CLI
OUT DX,AX
MOV AL,color
STOSB
STI
@offscrn:
END;
PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be written }
{ color = color value for the pixel to be drawn }
{ StartVirtualX, StartVirtualY = upper left image corner }
{out: - }
{rem: The point (x,y) has been transformed to absolute screen coordinates and}
{ has been drawn into the background (if it is onscreen) }
{ The pixel will NOT be visible until the next animation cycle takes }
{ place (but then, it remains permanent) (For that reason, you should }
{ call this routine BEFORE calling ANIMATE. That way, evtl. changes to }
{ the background will be visible "at once"!) }
{ Because BACKGNDPAGE is used as background page, calling this }
{ routine only makes sense when using background mode STATIC!}
ASM
MOV DI,y
SUB DI,StartVirtualY {transform y into absolue coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BX,3
MOV AH,[OFFSET TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
MOV ES,BACKGNDADR
CLI
OUT DX,AX
MOV AL,color
STOSB
STI
@offscrn:
END;
PROCEDURE PagePutPixel(x,y:INTEGER; color,pa:Byte); ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be written }
{ color = color value for the pixel to be drawn }
{ pa = graphic page (0..3) to be drawn upon }
{ PAGEADR= graphic page(segment) to be drawn upon }
{ StartVirtualX, StartVirtualY = upper left image corner }
{out: - }
{rem: The point (x,y) has been transformed to absolute screen coordinates }
{ and has been drawn (if it lies within the visible window) }
{ If you want to draw at the actually VISIBLE graphic page }
{ then you must call the routine with pa=1-PAGE! }
{ Again, the drawn pixel will _NOT_ automatically be taken }
{ over into the background image, that is: it will be visible }
{ only until the next animation cycle (=till calling ANIMATE) }
{ (For that reason, you should call this routine AFTER }
{ calling ANIMATE, because otherwise, your drawn pixel will }
{ vanish at once!) }
{ Sensible values for "pa" are either 0 or 1 (and evtl. BACK- }
{ GNDPAGE, if you are using background mode STATIC), however, }
{ the routine doesn't check that! }
ASM
MOV DI,y
SUB DI,StartVirtualY {transform y into absolue coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y*LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y*LINESIZE+(X SHR 2) }
AND BX,3
MOV AH,[OFFSET TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
MOV BL,pa {BH=0 -> BX=graphic page}
SHL BX,1
ADD BX,OFFSET Segment_Adr+StartIndex*2
MOV ES,[BX]
CLI
OUT DX,AX
MOV AL,color
STOSB
STI
@offscrn:
END;
PROCEDURE OutTextXY(x,y:INTEGER; pa:BYTE; s:STRING);
{ in: (x,y) = (virtual) starting coordinates for the text to be written}
{ s = textstring to be displayed }
{ pa = graphic page where the text shall be written }
{ GraphTextColor=color for text }
{ GraphTextBackground=color to be used for text background; if }
{ this value equals GraphTextColor, only the text-pixels }
{ themselves will be drawn while the surrounding ones }
{ don't change (=normal behaviour of TP's OutText-procs) }
{ GraphTextOrientation="vertical" or "horizontal" }
{ StartVirtualX,StartVirtualY = upper left image corner }
{out: text has been written to the screen }
VAR z,b,bit,i:BYTE;
data:Fontchar;
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber;
exit
END;
FOR i:=1 TO Length(s) DO
BEGIN
data:=FontData[ord(s[i])];
FOR z:=0 TO FontHeight-1 DO
BEGIN
b:=data[z];
FOR bit:=0 TO FontWidth-1 DO
IF b and FontMask[bit]<>0
THEN PagePutPixel(x+bit,y+z,GraphTextColor,pa)
ELSE IF (GraphTextColor<>GraphTextBackground)
THEN PagePutPixel(x+bit,y+z,GraphTextBackground,pa);
END;
IF GraphTextOrientation=horizontal
THEN INC(x,FontWidth)
ELSE INC(y,FontHeight);
END;
END;
PROCEDURE BackgroundOutTextXY(x,y:INTEGER; s:STRING);
{rem: Functionally equivalent to OutTextXY(), but the text will be }
{ written to the background page instead of page PAGEADR! }
{ Because BACKGNDADR is used as background page, calling }
{ this routine only makes sense when using mode STATIC! }
VAR z,b,bit,i:BYTE;
data:Fontchar;
BEGIN
FOR i:=1 TO Length(s) DO
BEGIN
data:=FontData[ord(s[i])];
FOR z:=0 TO FontHeight-1 DO
BEGIN
b:=data[z];
FOR bit:=0 TO FontWidth-1 DO
IF b and FontMask[bit]<>0
THEN BackgroundPutPixel(x+bit,y+z,GraphTextColor)
ELSE IF (GraphTextColor<>GraphTextBackground)
THEN BackgroundPutPixel(x+bit,y+z,GraphTextBackground);
END;
IF GraphTextOrientation=horizontal
THEN INC(x,FontWidth)
ELSE INC(y,FontHeight);
END;
END;
FUNCTION Hitdetect(s1,s2:INTEGER):BOOLEAN; ASSEMBLER;
{ in: s1,s2 = sprite position numbers of two sprites}
{ SpriteN[s1],SpriteX[s1],SpriteY[s1] = sprite data of sprite s1 }
{ SpriteN[s2],SpriteX[s2],SpriteY[s2] = sprite data of sprite s2 }
{out: TRUE/FALSE for "sprites collide"/"sprites do not collide" }
{rem: This check is pixel-precise and doesn't depend on the sprites being}
{ visible (=onscreen) or not! }
{ inactive sprites (SpriteN[s?]=0) cannot collide }
{ A sprite can't collide with itself, (thus: s1=s2 -> FALSE) }
ASM
MOV SI,s1 {get 1st parameter s1 from stack}
MOV DI,s2 {get 2nd parameter s2 from stack}
CMP SI,DI
JE @NOHIT1 {sprite can't collide with itself }
SHL SI,1
mov cx,[SI + OFFSET SpriteN]
jcxz @NOHIT1 {sprite <>0, that is: sprite active?}
SHL DI,1
MOV BX,[DI + OFFSET SpriteN]
OR BX,BX {dto. for other sprite }
JNE @PRUEF2
@NOHIT1:
JMP @NOHIT7 {inactive sprites can't collide }
{either -> return FALSE }
{here: SI (DI) = pointer to 1. (2.) sprite in ?YWRTD[..],}
{ CX (BX) = spritenumber of sprite 1 (2) }
{(a bit later, DS (ES) becomes segment addr. of sprite data of spr. 1 (2) )}
@PRUEF2:
MOV AX,[SI + OFFSET SpriteY]
MOV DX,[DI + OFFSET SpriteY]
mov si,[SI + OFFSET SpriteX] {SI=x1}
mov di,[DI + OFFSET SpriteX] {DI=x2}
shl bx,1 {BX=Spritenumber2*2}
mov es,[BX + OFFSET SPRITEAD] {ES=segment of 2nd sprite's data}
mov bx,cx {(CX=spritenumber1)}
shl bx,1 {BX=spritenumber1*2}
MOV ds,[BX + OFFSET SPRITEAD]
mov [y1],ax
mov [y2],dx
sub dx,ax
mov CS:WORD PTR @y2_y1+1,dx
mov [x1],si
mov [x2],di
mov dx,di
sub dx,si
mov CS:WORD PTR @x2_x1+1,dx
mov ax,es:[Left] {AX=pointer to left boundary data}
mov CS:WORD PTR @lirand2+1,ax
mov ax,es:[Right] {AX=pointer to right boundary data}
mov CS:WORD PTR @rerand2+1,ax
mov ax,es:[Top] {AX=pointer to upper boundary data}
mov CS:WORD PTR @orand2+1,ax
mov ax,es:[Bottom] {AX=pointer to lower boundary data}
mov CS:WORD PTR @urand2+1,ax
mov ax,es:[Breite] {AX=max. width in groups of 4 }
shl al,1
shl al,1
mov CS:WORD PTR @breite2+1,ax {*4 = width in points }
mov ax,es:[Hoehe]
mov CS:WORD PTR @hoehe2+1,ax {height of sprite2 in points }
MOV AX,[Left] {AX=pointer to left boundary data}
MOV CS:WORD PTR @LIRAND1+1,AX
MOV AX,[Right] {AX=pointer to right boundary data}
MOV CS:WORD PTR @RERAND1+1,AX
MOV AX,[Top] {AX=pointer to upper boundary data}
MOV CS:WORD PTR @ORAND1+1,AX
MOV AX,[Bottom] {AX=pointer to lower boundary data}
MOV CS:WORD PTR @URAND1+1,AX
MOV BX,[Breite] {BX=max. width in groups of 4 }
SHL BX,1
SHL BX,1 {*4 = width in points }
MOV CS:WORD PTR @BREITE1+2,BX
lea bx,[si+bx-1] {BX:=x1+breite1-1 (=x1last)}
@breite2:
mov bp,1234h {dummy value}
mov cx,bp {CX=breite2 will be needed later again }
lea bp,[di+bp-1] {BP:=x2+breite2-1 (=x2last)}
cmp bx,bp
jle @noex1
mov bp,bx
@noex1: {here: BP=max(x1last,x2last) (=maxx)}
cmp si,di
jle @X1_klgl_X2
xchg si,di
@X1_klgl_X2: {here: SI=min(x1,x2) (=minx)}
stc
sbb si,bp {SI:=minx-maxx-1=-(maxx-minx+1)}
@breite1:
add cx,1234h {(dummy value) CX:=breite1+breite2}
add cx,si {CX:=breite1+breite2-(maxx-minx+1)}
dec cx {CX:=breite1+breite2-(maxx-minx+1)-1 (=ueberlappx-1)}
js @NOHIT2 {no collision, if ueberlappx<=0 }
mov [ueberlappx_1],cx
mov ax,[Hoehe]
mov bx,ax {BX:=hoehe1}
mov di,[y1] {DI:=y1}
add ax,di {AX:=y1+hoehe1}
dec ax {AX:=y1+hoehe1-1 (=y1last)}
@hoehe2:
mov si,1234h
mov dx,[y2]
add dx,si {DX:=y2+hoehe2}
dec dx {DX:=y2+hoehe2-1 (=y2last)}
cmp ax,dx
jge @noex2
mov ax,dx
@noex2: {here: AX=max(y1last,y2last) (=maxy)}
mov dx,[y2]
cmp di,dx {(DI=y1)}
jle @noex3
mov di,dx
@noex3: {here: DI=min(y1,y2) (=miny)}
sub di,ax {DI:=miny-maxy=-(maxy-miny)}
lea ax,[bx+si-2] {AX:=hoehe1+hoehe2-2}
add ax,di {AX:=hoehe1+hoehe2-(maxy-miny+1)-1 (=ueberlappy-1)}
js @NOHIT2 {no collision, if ueberlappy<=0 }
mov [ueberlappy_1],ax
{here: AX=ueberlappy-1, CX=ueberlappx-1}
@x2_x1:
mov dx,1234h {dummy value}
xor bx,bx {from now on: BX=0!}
or dx,dx
js @X2_X1_kl_0 {if x2-x1>=0 then...}
mov [hit2xfirst],bx {...hit2xfirst:=0}
mov [hit1xfirst],dx {...hit1xfirst:=x2-x1}
jmp @Yhits {SHORT}
{jump-rail for NOHIT (this is a good place) }
@NOHIT2:
JMP @NOHIT7
{now back at "normal" program }
@X2_X1_kl_0: {else (x2-x1<0)...}
mov [hit1xfirst],bx {...hit1xfirst:=0}
neg dx {DX:=x1-x2}
mov [hit2xfirst],dx {...hit2xfirst:=x1-x2}
@Yhits: {here: AX=ueberlappy-1}
@y2_y1:
mov dx,1234h {dummy value}
or dx,dx
js @Y2_Y1_kl_0 {if y2-y1>=0 then...}
mov [hit2yfirst],bx {...hit2yfirst:=0}
mov [hit1yfirst],dx {...hit1yfirst:=y2-y1}
jmp @iterate {SHORT}
@Y2_Y1_kl_0: {else (y2-y1<0)...}
mov [hit1yfirst],bx {...hit1yfirst:=0}
neg dx {DX:=y1-y2}
mov [hit2yfirst],dx {...hit2yfirst:=y1-y2}
{Now check the overlapping rows and columns more closely by iteration: }
@iterate:
mov cx,[ueberlappy_1] {number of rows -1 to compare }
shl cx,1 {*2, because word-sized!}
@lirand1:
mov si,1234h {dummy value}
@lirand2:
mov di,1234h {dummy value}
@rerand1:
mov bx,1234h {dummy value}
@rerand2:
mov bp,1234h {dummy value}
sub bx,si {BX:=rerand1-lirand1}
sub bp,di {BP:=rerand2-lirand2}
mov ax,[hit1yfirst]
shl ax,1
add si,ax {SI:=1st row where sprite1 overlaps sprite2 }
mov ax,[hit2yfirst]
shl ax,1
add di,ax {DI:=1st row where sprite2 overlaps sprite1 }
add si,cx {dto., last row }
add di,cx
@one_line:
mov ax,[si] {DS:AX:=x1li[row] }
mov dx,es:[di] {ES:DX:=x2li[row] }
add ax,[x1] {AX:=x1li[row]+x1 (=c) }
add dx,[x2] {DX:=x2li[row]+x2 (=d) }
cmp ax,dx
jge @C_grgl_D
mov ax,dx
@C_grgl_D: {here: AX=max(c,d)}
mov cx,[si+bx] {DS:CX:=x1re[row] }
mov dx,es:[di+bp] {ES:DX:=x2re[row] }
add cx,[x1] {CX:=x1re[row]+x1 (=a) }
add dx,[x2] {DX:=x2re[row]+x2 (=b) }
cmp cx,dx
jle @A_klgl_B
mov cx,dx
@A_klgl_B: {here: CX=min(a,b)}
cmp cx,ax {min(a,b)>=max(c,d) ?}
jge @found_Xhit {yes: collision in X-direction found!}
dec si {next row (-> word-sized values!)}
dec si
dec di
dec di
dec WORD PTR [ueberlappy_1]
jns @one_line
{no collision in X-direction -> no collision at all! }
jmp @NOHIT7
{otherwise: collision in X-direction, now check Y-dir. also (as above) and }
{report "collision!" only, if there is at least 1 collision in Y-dir., too }
@found_Xhit:
mov cx,[ueberlappx_1] {number of columns -1 to compare }
shl cx,1 {*2, because word-sized!}
@orand1:
mov si,1234h {dummy value}
@orand2:
mov di,1234h {dummy value}
@urand1:
mov bx,1234h {dummy value}
@urand2:
mov bp,1234h {dummy value}
sub bx,si {BX:=urand1-orand1}
sub bp,di {BP:=urand2-orand2}
mov ax,[hit1xfirst]
shl ax,1 {*2, because word-sized!}
add si,ax {SI:=orand1+2*hit1xfirst}
mov ax,[hit2xfirst]
shl ax,1 {*2, because word-sized!}
add di,ax {DI:=orand2+2*hit2xfirst}
add si,cx
add di,cx
@one_column: mov ax,[si] {AX:=y1ob[column]}
cmp ax,16000 {dummy value for "empty column"?}
je @next_column {yes, thus: surely no collision }
mov dx,es:[di] {DX:=y2ob[column]}
cmp dx,16000 {check 2nd sprite too: "empty column"? }
je @next_column {yes, no collision}
add ax,[y1] {AX:=y1ob+y1 (=c)}
add dx,[y2] {DX:=y2ob+y2 (=d)}
cmp ax,dx
jge @C_grgl_D2
mov ax,dx
@C_grgl_D2: {here: AX=max(c,d)}
mov cx,[si+bx] {DS:CX:=y1un[column]}
mov dx,es:[di+bp] {ES:DX:=y2un[column]}
add cx,[y1] {CX:=y1un+y1 (=a)}
add dx,[y2] {DX:=y2un+y2 (=b)}
cmp cx,dx
jle @A_klgl_B2
mov cx,dx
@A_klgl_B2: {here: CX=min(a,b)}
cmp cx,ax {min(a,b)>=max(c,d) ?}
jge @HIT2 {yes: collision detected!}
@next_column:
dec si {no, next column (-> word-sized values!)}
dec si
dec di
dec di
dec WORD PTR [ueberlappx_1]
jns @one_column
@NOHIT7:
XOR AX,AX {return 0 = FALSE as result }
JMP @TREFF_END {SHORT}
@HIT2:
MOV AX,1 {return 1 = TRUE as result }
@TREFF_END:
{$IFOPT G+}
mov bp,sp {only necessary for compiler switch G+!}
{$ENDIF}
mov dx,seg @DATA {else, BP will be restored by TP itself}
mov ds,dx
END;
PROCEDURE Animate;
{ in: PAGEADR = actual graphicpage(address) on which to draw upon }
{ BACKGNDADR = background page(address) }
{ BACKGROUNDMODE = STATIC/SCROLLING for solid/scrollable background }
{ SpriteN[] = spritenumber of sprite to be displayed }
{ SpriteX[],SpriteY[] = their (virtual) coordinates }
{ StartVirtualX,StartVirtualY = upper left image corner }
{ (PAGE = actually displayed graphic page) }
{out: PAGE = 0/1, if PAGE has been 1/0, respectively }
{ PAGEADR = new, actual graphic page(address) }
{rem: Animate erases the old contents of the page (using the background page }
{ information), draws all visible sprites, synchronizes to the display- }
{ enable signal and switches the display to that now completed page }
VAR offsetXTiles,offsetYTiles,offsetXPix,offsetYPix:INTEGER;
leftcut,rightcut,topcut,bottomcut,tiles:WORD;
x,y,xpix,ypix,xtil,ytil,actindex,randindex,index:INTEGER;
offscreenFlag:BYTE;
yt,xt:INTEGER;
BEGIN
ASM
CLD
{first copy the background picture to the actual graphic page:}
CMP BackgroundMode,STATIC {which background mode? }
JE @static_bckgnd
JMP @scrolling_bckgnd
@static_bckgnd:
MOV AX,0F02h {write to all 4 planes simultaneously }
MOV DX,3C4h
OUT DX,AX
MOV AX,4105h {choose write mode 1 }
MOV DX,3CEh
OUT DX,AX
MOV ES,PAGEADR {fill graphic page with background pattern}
MOV DS,BACKGNDADR
XOR SI,SI
MOV DI,SI
MOV CX,PAGESIZE
REP MOVSB
MOV AX,SEG @DATA
MOV DS,AX
MOV AX,4005h {select write mode 0}
MOV DX,3CEh
OUT DX,AX
JMP @Sprites_zeichnen
{---------------------------------}
@scrolling_bckgnd: {now: create background image from tiles }
MOV AX,StartVirtualY
MOV BX,AX {AX=BX=StartVirtualY}
SUB AX,BackY1
ADD AX,15 {offsetYTiles:=(StartVirtualY-BackY1+15) DIV 16}
SAR AX,1
SAR AX,1
SAR AX,1
SAR AX,1
MOV offsetYTiles,AX
MOV ytil,AX {ytil:=offsetYTiles}
DEC AX
IMUL XTiles
MOV actIndex,AX {actIndex:=(ytil-1)*XTiles, "+xtil" comes later }
MOV AX,16
SUB AX,BX {BX=StartVirtualY}
AND AX,$F
MOV ypix,AX {ypix:=(16-StartVirtualY) AND $F}
SUB AX,200
AND AX,$F
MOV bottomcut,AX {bottomcut:=(ypix-200) AND $F}
AND BX,$F {offsetYPix:=topcut:=StartVirtualY AND $F}
MOV topcut,BX
MOV offsetYPix,BX
MOV AX,StartVirtualX
MOV BX,AX {AX=BX=StartVirtualX}
SUB AX,BackX1
ADD AX,15 {offsetXTiles:=(StartVirtualX-BackX1+15) DIV 16}
SAR AX,1
SAR AX,1
SAR AX,1
SAR AX,1
MOV offsetXTiles,AX
MOV xtil,AX {xtil:=offsetXTiles}
ADD actIndex,AX {actIndex:=(ytil-1)*XTiles+xtil}
MOV AX,16
SUB AX,BX {BX=StartVirtualX}
AND AX,$F
MOV xpix,AX {xpix=rightcut:=(16-StartVirtualX) AND $F}
MOV rightcut,AX
AND BX,$F {offsetXPix:=leftcut:=StartVirtualX AND $F}
MOV leftcut,BX
MOV offsetXPix,BX
MOV AX,(XMAX+1)/16-1
SUB BL,1 {C=1, if leftcut=0 }
ADC AX,0
MOV tiles,AX {tiles:=19+ord(leftcut=0)}
CMP topcut,0 {if topcut=0, the upper tile row doesn't}
JE @do_innertiles {need to be drawn separately }
{top most tile row:}
MOV DX,xtil
MOV xt,DX
MOV AX,ytil
DEC AX
MOV yt,AX
MOV CL,1
JS @offscreen
CMP AX,YTiles
JAE @offscreen
DEC CL
@offscreen: {CL=0/1 for offscreenFlag=false/true=(yt<0) OR (yt>=YTiles) }
MOV offscreenFlag,CL
CMP leftcut,0 {if leftcut=0, the upper left corner does}
JE @do_upperinnertiles {not need to be drawed separately }
{draw upper left corner-tile: } {CL=offscreenFlag, DX=xt}
XOR SI,SI {determine tile index:}
DEC CL {IF offscreenFlag OR (xt-1<0) OR (xt-1>=XTiles) }
JZ @go1 { THEN index:=0 ELSE index:=actIndex}
DEC DX
JS @go1
CMP DX,XTiles
JAE @go1
MOV SI,actIndex {=yt*XTiles+(xt-1)}
@go1:
{PROCEDURE DrawUpperLeftTile(leftcut,topcut:INTEGER; index:WORD);}
{ in: leftcut = number of left tile-columns to be cut off }
{ topcut = dto., at the top}
{ SI = index = tile number}
{out: tile has been drawn at (0,0) on the actual page PAGEADR }
{rem: tile has been clipped at its left and top accordingly}
{ leftcut must lie in the range 0..15, 16 is allowed (but senseless) }
{ topcut must lie in the range 0..15, 16 is not allowed}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2 }
RCR AL,1 {therefore: set AH:=tile and afterwards, shift AX }
SHR AH,1 {2 bits to the right!}
RCR AL,1
MOV SI,topcut
MOV CX,16
SUB CX,SI {CX:=16-topcut = number of rows to draw }
SHL SI,1
SHL SI,1 {add 4 bytes to the (offset part of the) }
ADD SI,AX {source addr. in page 3 for each cut off top-row}
XOR DI,DI {the first dest. addr. is DI:=0*LINESIZE+(0 div 4)=0 }
MOV AX,leftcut
MOV BX,AX {have a copy of leftcut in BX }
SHR AX,1
SHR AX,1
ADD SI,AX {increment SI by cutoff (=leftcut div 4) bytes }
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,16+3
SUB BP,BX {BP:=16+3-leftcut, because the number of bytes per row }
{of plane i is computed by (16+3-i-leftcut) SHR 2 }
MOV ES,PAGEADR {(segment part of) dest. address is active page}
MOV DS,SCROLLADR {(segment part of) source addr. is page SCROLLPAGE}
MOV DX,3CEh
AND BL,3
MOV AH,BL {AH:=leftcut mod 4}
MOV AL,4
JNE @mode0a {only if leftcut mod 4=0 is it possible to use WriteMode1 }
{--- short-cut possible, using write mode1: DX=3CEh, BP=16+3-leftcut }
{ BP being "3 units to big" doesn't matter, because this code will }
{ only be run if leftcut mod 4=0, thus 16+3-leftcut=...11b, the }
{ "11b" will be cut off while shifting right, anyway! }
MOV AX,4105h
OUT DX,AX {choose write mode 1}
MOV DX,3C4h
MOV AX,0F02h {work on all 4 planes simultaneously }
OUT DX,AX
SHR BP,1 {use BP directly }
SHR BP,1 {BP:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
{don't save source- and destination addresses}
MOV BX,CX {BX:=row counter }
@eineZeile4a1:
MOV CX,BP {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BX
JNZ @eineZeile4a1
{--- short cut has been taken: thus, reset write mode 0! }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
JMP @UpperLeftTileDone
@mode0a:
OUT DX,AX {choose plane from which to read}
PUSH AX {and preserve it for later}
MOV DX,3C4h
MOV AX,0102h {choose write plane 0 }
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane0_row = (16+3-leftcut) DIV 4 }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane0_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane0_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {CX = save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile1a:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile1a
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0202h {choose write plane 1 }
OUT DX,AX
MOV DX,3CEh {next read plane: }
POP AX
INC AH
AND AH,3 {increment by 1 MOD 4}
JNE @nowrap1a
INC SI {plane 0 follows after plane 3 again, but}
{the source address has increased by 1 byte }
@nowrap1a:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+2-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane1_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane1_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane1_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile2a:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile2a
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0402h {choose write plane 2 }
OUT DX,AX
MOV DX,3CEh {next read plane: }
POP AX
INC AH
AND AH,3 {increment by 1 MOD 4}
JNE @nowrap2a
INC SI {plane 0 follows after plane 3 again, but}
{the source address has increased by 1 byte }
@nowrap2a:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+1-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane2_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane2_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane2_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile3a:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile3a
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0802h {choose write plane 3 }
OUT DX,AX
MOV DX,3CEh {next read plane: }
POP AX
INC AH
AND AH,3 {increment by 1 MOD 4}
JNE @nowrap3a
INC SI {plane 0 follows after plane 3 again, but}
{the source address has increased by 1 byte }
@nowrap3a:
OUT DX,AX
DEC BP {BP:=16-leftcut}
@lastplane1:
SHR BP,1 {use BP directly }
SHR BP,1 {BP:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
{don't save source- and destination addresses}
MOV BX,CX {BX:=row counter }
@eineZeile4a:
MOV CX,BP {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BX
JNZ @eineZeile4a
@UpperLeftTileDone:
POP BP
MOV AX,SEG @Data {restore DS }
MOV DS,AX
{Now work on all other upper tiles, which are cut at their top (but }
{not at the sides!): }
@do_upperinnertiles:
MOV AX,xpix
MOV x,AX
INC actIndex
@repeat1: {loop will be taken exactly "tiles" times }
MOV CL,offscreenFlag
XOR SI,SI {determine tile index:}
DEC CL {IF offscreenFlag OR (xt<0) OR (xt>=XTiles) }
JZ @go2 { THEN index:=0 ELSE index:=actIndex =yt*XTiles+xt}
MOV DX,xt
OR DX,DX
JS @go2
CMP DX,XTiles
JAE @go2
MOV SI,actIndex
@go2:
{PROCEDURE DrawUpperTile(x,topcut:INTEGER; index:WORD);}
{ in: (x,0) = upper left corner of the tile to be drawn,}
{ topcut= number of lines to be cut off}
{ SI = index = tile number}
{out: tile has been drawn on actual page PAGEADR }
{rem: Tile must be onscreen with its left, right (and lower) parts }
{ topcut must lie in stack (because DS is used for other purposes!)}
{ topcut must have a value in the range 0..15, 16 is not allowed!}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2 }
RCR AL,1 {therefore: set AH:=tile and afterwards, shift AX }
SHR AH,1 {2 bits to the right!}
RCR AL,1 {That's also the offset part of the source ad. in page3}
MOV SI,topcut {additionaly: the rows cut off at the upper edge:}
MOV CX,16 {4 bytes for each row }
SUB CX,SI {CX:=16-topcut = rows to draw }
SHL SI,1
SHL SI,1
ADD SI,AX {SI = pointer to first tile byte to copy }
{The first destination address is DI:=0*LINESIZE+(x div 4)=x div 4}
MOV DI,x
MOV BX,DI {have a copy of X in BX }
SHR DI,1
SHR DI,1
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,CX
SHL BP,1
SHL BP,1 {BP := (rows to draw)*4 }
MOV ES,PAGEADR {(segment part of) dest. address is active page}
MOV DS,SCROLLADR {(segment part of) source addr. is page SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3 {BX:=1st write plane (1st READ plane=0!) }
JNE @mode0b {only if x mod 4=0 is it possible to use WriteMode1 }
{--- short-cut possible, using write mode1 }
MOV AH,0Fh {work on all 4 planes simultaneously }
OUT DX,AX
MOV AX,4105h
MOV DX,3CEh
OUT DX,AX {choose write mode 1}
MOV BX,CX {BX:=CX=rows to draw }
MOV AX,LINESIZE-4
MOV CX,BX {CX:=number of rows to draw }
@eineZeile4b1:
MOVSB {no MOVSW, as we use write mode 1!}
MOVSB
MOVSB
MOVSB
ADD DI,AX {set DI to next row }
LOOP @eineZeile4b1
{--- short cut has been taken: thus, reset write mode 0! }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
JMP @UpperTileDone
@mode0b:
MOV AH,CS:[OFFSET CS_TranslateTab+BX]
OUT DX,AX
PUSH AX {save actual write plane }
MOV DX,3CEh
MOV AX,0004h {choose plane 0 for reading }
OUT DX,AX
MOV AX,LINESIZE-4 {adjustment for row addresses }
MOV BX,CX {save number of rows to draw into BX }
@eineZeile1b:
MOVSW
MOVSW
ADD DI,AX {set DI to next row }
LOOP @eineZeile1b
MOV AX,0104h {DX=3CEh -> choose read plane 1}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1b
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap1b:
OUT DX,AX
PUSH AX
SHL BX,1 {reset DI to the source address: }
SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)* }
SHR BX,1 {LINESIZE (N.B.: BX=number of rows to draw)}
SUB SI,BP {reset SI, too }
MOV AX,LINESIZE-4
MOV CX,BX {CX:=number of rows to draw }
@eineZeile2b:
MOVSW
MOVSW
ADD DI,AX {set DI to next row }
LOOP @eineZeile2b
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2b
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap2b:
OUT DX,AX {DX=3C4h -> set write plane }
PUSH AX
MOV DX,3CEh
MOV AX,0204h {set read plane 2 }
OUT DX,AX
SHL BX,1 {reset DI to the source address: }
SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)* }
SHR BX,1 {LINESIZE (N.B.: BX=number of rows to draw)}
SUB SI,BP {reset SI, too }
MOV AX,LINESIZE-4
MOV CX,BX {CX:=number of rows to draw }
@eineZeile3b:
MOVSW
MOVSW
ADD DI,AX {set DI to next row }
LOOP @eineZeile3b
MOV AX,0304h
OUT DX,AX {DX=3CEh -> choose read plane 3}
MOV DX,3C4h
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3b
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap3b:
OUT DX,AX {DX=3C4h -> set write plane }
SHL BX,1 {reset DI to the source address: }
SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)* }
SHR BX,1 {LINESIZE (N.B.: BX=number of rows to draw)}
SUB SI,BP {reset SI, too }
@lastPlane2:
MOV AX,LINESIZE-4
MOV CX,BX {CX:=number of rows to draw }
@eineZeile4b:
MOVSW
MOVSW
ADD DI,AX {set DI to next row }
LOOP @eineZeile4b
@UpperTileDone:
POP BP
MOV AX,SEG @Data {restore DS }
MOV DS,AX
INC xt {increment tile counter in X-direction}
INC actIndex {because actIndex=yt*XTiles+xt do increment actIndex, too}
MOV AX,x {increment actual X-coordinate, too: x:=x+16 }
ADD AX,16
MOV x,AX
CMP AX,XMAX+1-16
JBE @repeat1 {until upper right corner has been reached}
{Now the upper right corner-tile - if not already drawn: }
CMP AX,XMAX+1
JE @label1
MOV CL,offscreenFlag {yes, corner remains to be drawn }
XOR SI,SI {determine tile index:}
DEC CL {IF offscreenFlag OR (xt<0) OR (xt>=XTiles) }
JZ @go3 { THEN index:=0 ELSE index:=actIndex =yt*XTiles+xt}
MOV DX,xt
OR DX,DX
JS @go3
CMP DX,XTiles
JAE @go3
MOV SI,actIndex
@go3:
{PROCEDURE DrawUpperRightTile(x,rightcut,topcut:INTEGER; index:WORD);}
{ in: (x,0) = upper left corner of the tile to be drawn}
{ rightcut = number of right tile-columns to be cut off }
{ topcut = dto., at the top}
{ SI = index = tile number}
{out: tile has been drawn on actual page PAGEADR }
{rem: tile has been clipped at its right and top accordingly}
{ topcut must lie in the range 0..15, 16 is not allowed}
{ rightcut must lie between 0..15, 16 is allowed (but senseless)! }
{ rightcut could also be computed (for x>xmax-16) by }
{ rightcut := x+15-xmax }
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2 }
RCR AL,1 {therefore: set AH:=tile and afterwards, shift AX }
SHR AH,1 {2 bits to the right!}
RCR AL,1
MOV SI,topcut
MOV CX,16
SUB CX,SI {CX:=16-topcut = number of rows to draw }
SHL SI,1
SHL SI,1 {add 4 bytes to the (offset part of the) }
ADD SI,AX {source addr. in page 3 for each cut off top-row}
MOV DI,x {first dest. addr. is DI:=0*LINESIZE +(x div 4)=x div 4 }
MOV BX,DI {place a copy of x in BX}
SHR DI,1
SHR DI,1
MOV AX,rightcut
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,16+3
SUB BP,AX {BP:=16+3-rightcut, because the number of bytes per row }
{of plane i is computed by (16+3-i-rightcut) SHR 2 }
MOV AH,AL {save rightcut to AH }
MOV ES,PAGEADR {(segment part of) dest. address is active page}
MOV DS,SCROLLADR {(segment part of) source addr. is page SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3
JNE @mode0c {only if x mod 4=0 _and_ rightcut mod 4=0, then we can }
AND AH,3 {use write mode 1! }
JNE @mode0c
{--- short-cut possible, using write mode1: DX=3C4h, BP=16+3-rightcut }
MOV AH,0Fh
OUT DX,AX {work on all 4 planes simultaneously }
MOV DX,3CEh
MOV AX,4105h {choose write mode 1}
OUT DX,AX
SUB BP,3 {set BP to proper size }
SHR BP,1 {use BP directly }
SHR BP,1 {BP:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
{don't save source- and destination addresses}
MOV BX,CX {BX:=row counter }
@eineZeile4c1:
MOV CX,BP {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BX
JNZ @eineZeile4c1
{--- short cut has been taken: thus, reset write mode 0! }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
JMP @UpperRightDone
@mode0c:
MOV AH,CS:[OFFSET CS_TranslateTab +BX]
OUT DX,AX {choose plane to write at }
PUSH AX {and preserve it for later}
MOV DX,3CEh
MOV AX,0004h {choose read plane 0}
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane0_row = (16+3-rightcut) DIV 4 }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane0_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane0_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {CX = save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile1c:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile1c
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0104h {choose read plane 1}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1c
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap1c:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+2-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane1_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane1_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane1_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {CX = save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile2c:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile2c
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0204h {choose read plane 2}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2c
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap2c:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+1-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane2_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane2_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane2_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {CX = save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile3c:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile3c
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0304h {choose read plane 3}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3c
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap3c:
OUT DX,AX
{do not push value again!}
DEC BP {BP:=16-rightcut}
@lastplane3:
SHR BP,1 {use BP directly }
SHR BP,1 {BP:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
{don't save source- and destination addresses}
MOV BX,CX {BX:=row counter }
@eineZeile4c:
MOV CX,BP {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BX
JNZ @eineZeile4c
@UpperRightDone:
POP BP
MOV AX,SEG @Data {restore DS }
MOV DS,AX
@label1:
MOV AX,actIndex {adjust actIndex for next tile-row: }
STC {actIndex:=actIndex-tiles+1}
SBB AX,tiles
MOV actIndex,AX
{Now draw all tiles which are completely onscreen }
{(that is: all non-cut) tiles: }
@do_innertiles:
MOV AX,ypix
MOV y,AX
MOV AX,actIndex
ADD AX,XTiles
MOV RandIndex,AX
INC AX
MOV actIndex,AX
@repeat2:
MOV AX,ytil
MOV CL,1
OR AX,AX
JS @go4
CMP AX,YTiles
JAE @go4
DEC CL
@go4:
MOV offscreenFlag,CL
MOV AX,xpix
MOV x,AX
MOV AX,offsetXTiles
MOV xtil,AX
@repeat3:
MOV CL,offscreenFlag
XOR SI,SI
DEC CL
JZ @go5
OR AX,AX
JS @go5
CMP AX,XTiles
JAE @go5
MOV SI,actIndex
@go5:
{PROCEDURE DrawInnerTile(x,y:INTEGER; index:WORD);}
{ in: (x,y) = upper left corner of the tile to be drawn,}
{ SI = index = tile number}
{out: tile has been drawn on actual page PAGEADR }
{rem: tile must be completely onscreen, there will be }
{ no check for that }
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2 }
RCR AL,1 {therefore: set AH:=tile and afterwards, shift AX }
SHR AH,1 {2 bits to the right!}
RCR AL,1
MOV SI,AX {That's also the offset part of the source ad. in page3}
MOV DI,y {the first destination addr. is DI:=y*LINESIZE+(x div 4)}
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,x
MOV BX,AX {have a copy of X in BX }
SHR AX,1
SHR AX,1
ADD DI,AX
MOV ES,PAGEADR {(segment part of) dest. address is active page}
MOV DS,SCROLLADR {(segment part of) source addr. is page SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3 {BX:=1st write plane (1st READ plane=0!) }
JNE @mode0d {only if x mod 4=0 is it possible to use WriteMode1 }
{--- short-cut possible, using write mode1 }
MOV AH,0Fh {work on all 4 planes simultaneously }
OUT DX,AX
MOV AX,4105h
MOV DX,3CEh
OUT DX,AX {choose write mode 1}
MOV BX,4
MOV AX,LINESIZE-4
MOV BX,4 {16 pixels = 4 bytes}
MOV CX,BX {no "MOVSW", as we use write mode1}
REP MOVSB {draw 1st row }
ADD DI,AX {set DI to next row }
MOV CX,BX
REP MOVSB {2.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {3.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {4.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {5.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {6.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {7.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {8.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {9.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {10.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {11.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {12.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {13.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {14.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {15.row }
ADD DI,AX
MOV CX,BX
REP MOVSB {16.row }
{--- short cut has been taken: thus, reset write mode 0! }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
JMP @InnerTileDone
@mode0d:
MOV AH,CS:[OFFSET CS_TranslateTab+BX]
OUT DX,AX
PUSH AX {save actual write plane }
MOV DX,3CEh
MOV AX,0004h {choose plane 0 for reading }
OUT DX,AX
MOV AX,LINESIZE-4 {adjustment for row addresses }
MOVSW {16 horizontal pixels = 4 bytes per line }
MOVSW {draw 1st row }
ADD DI,AX {set DI to next row }
MOVSW {2.row }
MOVSW
ADD DI,AX
MOVSW
MOVSW {3.row }
ADD DI,AX
MOVSW
MOVSW {4.row }
ADD DI,AX
MOVSW
MOVSW {5.row }
ADD DI,AX
MOVSW
MOVSW {6.row }
ADD DI,AX
MOVSW
MOVSW {7.row }
ADD DI,AX
MOVSW
MOVSW {8.row }
ADD DI,AX
MOVSW
MOVSW {9.row }
ADD DI,AX
MOVSW
MOVSW {10.row }
ADD DI,AX
MOVSW
MOVSW {11.row }
ADD DI,AX
MOVSW
MOVSW {12.row }
ADD DI,AX
MOVSW
MOVSW {13.row }
ADD DI,AX
MOVSW
MOVSW {14.row }
ADD DI,AX
MOVSW
MOVSW {15.row }
ADD DI,AX
MOVSW
MOVSW {16.row }
ADD DI,AX
MOV AX,0104h {DX=3CEh -> choose read plane 1}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1d
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap1d:
OUT DX,AX
PUSH AX
SUB DI,16*LINESIZE {reset DI to the source address }
SUB SI,16*4 {SI, too}
MOV AX,LINESIZE-4
MOVSW
MOVSW {draw 1st row }
ADD DI,AX {set DI to next row }
MOVSW
MOVSW {2.row }
ADD DI,AX
MOVSW
MOVSW {3.row }
ADD DI,AX
MOVSW
MOVSW {4.row }
ADD DI,AX
MOVSW
MOVSW {5.row }
ADD DI,AX
MOVSW
MOVSW {6.row }
ADD DI,AX
MOVSW
MOVSW {7.row }
ADD DI,AX
MOVSW
MOVSW {8.row }
ADD DI,AX
MOVSW
MOVSW {9.row }
ADD DI,AX
MOVSW
MOVSW {10.row }
ADD DI,AX
MOVSW
MOVSW {11.row }
ADD DI,AX
MOVSW
MOVSW {12.row }
ADD DI,AX
MOVSW
MOVSW {13.row }
ADD DI,AX
MOVSW
MOVSW {14.row }
ADD DI,AX
MOVSW
MOVSW {15.row }
ADD DI,AX
MOVSW
MOVSW {16.row }
ADD DI,AX
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2d
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap2d:
OUT DX,AX {DX=3C4h -> set write plane }
PUSH AX
MOV DX,3CEh
MOV AX,0204h {set read plane 2 }
OUT DX,AX
SUB DI,16*LINESIZE
SUB SI,16*4
MOV AX,LINESIZE-4
MOVSW
MOVSW {draw 1st row }
ADD DI,AX {set DI to next row }
MOVSW
MOVSW {2.row }
ADD DI,AX
MOVSW
MOVSW {3.row }
ADD DI,AX
MOVSW
MOVSW {4.row }
ADD DI,AX
MOVSW
MOVSW {5.row }
ADD DI,AX
MOVSW
MOVSW {6.row }
ADD DI,AX
MOVSW
MOVSW {7.row }
ADD DI,AX
MOVSW
MOVSW {8.row }
ADD DI,AX
MOVSW
MOVSW {9.row }
ADD DI,AX
MOVSW
MOVSW {10.row }
ADD DI,AX
MOVSW
MOVSW {11.row }
ADD DI,AX
MOVSW
MOVSW {12.row }
ADD DI,AX
MOVSW
MOVSW {13.row }
ADD DI,AX
MOVSW
MOVSW {14.row }
ADD DI,AX
MOVSW
MOVSW {15.row }
ADD DI,AX
MOVSW
MOVSW {16.row }
ADD DI,AX
MOV AX,0304h
OUT DX,AX {DX=3CEh -> choose read plane 3}
MOV DX,3C4h
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3d
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap3d:
OUT DX,AX {DX=3C4h -> set write plane }
SUB DI,16*LINESIZE
SUB SI,16*4
@lastPlane4:
MOV AX,LINESIZE-4
MOVSW
MOVSW {draw 1st row }
ADD DI,AX {set DI to next row }
MOVSW
MOVSW {2.row }
ADD DI,AX
MOVSW
MOVSW {3.row }
ADD DI,AX
MOVSW
MOVSW {4.row }
ADD DI,AX
MOVSW
MOVSW {5.row }
ADD DI,AX
MOVSW
MOVSW {6.row }
ADD DI,AX
MOVSW
MOVSW {7.row }
ADD DI,AX
MOVSW
MOVSW {8.row }
ADD DI,AX
MOVSW
MOVSW {9.row }
ADD DI,AX
MOVSW
MOVSW {10.row }
ADD DI,AX
MOVSW
MOVSW {11.row }
ADD DI,AX
MOVSW
MOVSW {12.row }
ADD DI,AX
MOVSW
MOVSW {13.row }
ADD DI,AX
MOVSW
MOVSW {14.row }
ADD DI,AX
MOVSW
MOVSW {15.row }
ADD DI,AX
MOVSW
MOVSW {16.row }
@InnerTileDone:
MOV AX,SEG @Data {restore DS }
MOV DS,AX
INC actIndex
MOV AX,xtil
INC AX
MOV xtil,AX
MOV DX,x
ADD DX,16
MOV x,DX
CMP DX,XMAX+1-16
JBE @repeat3
INC ytil
MOV AX,actIndex
SUB AX,tiles
ADD AX,XTiles
MOV actIndex,AX
MOV AX,y
ADD AX,16
MOV y,AX
CMP AX,YMAX+1-16
JBE @repeat2
{Now draw the lower right tile - if it doesn't have been drawn by one of}
{routines above: }
CMP bottomcut,0
JE @do_raender
MOV DX,offsetXTiles
MOV xt,DX
MOV AX,ytil
MOV yt,AX
MOV CL,1
OR AX,AX
JS @go6
CMP AX,YTiles
JAE @go6
DEC CL
@go6:
MOV offscreenFlag,CL
CMP leftcut,0
JE @label2
XOR SI,SI
DEC CL
JZ @go7
DEC DX {DX=xt-1}
JS @go7
CMP DX,XTiles
JAE @go7
MOV SI,actIndex
DEC SI
@go7:
{PROCEDURE DrawLowerLeftTile(y,leftcut,bottomcut:INTEGER; index:WORD);}
{ in: (0,y) = upper left corner of the tile to be drawn}
{ leftcut = number of left tile-columns to be cut off }
{ bottomcut = dto., bottom}
{ SI = index = tile number}
{out: tile has been drawn on actual page PAGEADR }
{rem: tile has been clipped at its left and bottom accordingly}
{ leftcut must lie in the range 0..15, 16 is allowed (but senseless) }
{ bottomcut must lie in the range 0..15, 16 is not allowed}
{ bottomcut could also be computed by: }
{ bottomcut:=y+15-ymax}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2 }
RCR AL,1 {therefore: set AH:=tile and afterwards, shift AX }
SHR AH,1 {2 bits to the right!}
RCR AL,1
MOV SI,AX {That's also the offset part of the source ad. in page3}
MOV DI,y {first dest. addr. is DI:=y*LINESIZE+(0 div 4)=y*LINESIZE }
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,leftcut
MOV BX,AX {have a copy of leftcut in BX }
SHR AX,1
SHR AX,1
ADD SI,AX {increment SI by cutoff (=leftcut div 4) bytes }
MOV CX,16
SUB CX,bottomcut {CX:=16-bottomcut = rows to draw }
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,16+3
SUB BP,BX {BP:=16+3-leftcut, because the number of bytes per row }
{of plane i is computed by (16+3-i-leftcut) SHR 2 }
MOV ES,PAGEADR {(segment part of) dest. address is active page}
MOV DS,SCROLLADR {(segment part of) source addr. is page SCROLLPAGE}
MOV DX,3CEh
AND BL,3
MOV AH,BL {AH:=leftcut mod 4}
MOV AL,4
JNE @mode0e {only if leftcut mod 4=0 is it possible to use WriteMode1 }
{--- short-cut possible, using write mode1: DX=3CEh, BP=16+3-leftcut }
{ BP being "3 units to big" doesn't matter, because this code will }
{ only be run if leftcut mod 4=0, thus 16+3-leftcut=...11b, the }
{ "11b" will be cut off while shifting right, anyway! }
MOV AX,4105h
OUT DX,AX {choose write mode 1}
MOV DX,3C4h
MOV AX,0F02h {work on all 4 planes simultaneously }
OUT DX,AX
SHR BP,1 {use BP directly }
SHR BP,1 {BP:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
MOV BX,CX {BX:=row counter }
@eineZeile4d1:
MOV CX,BP {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BX
JNZ @eineZeile4d1
{--- short cut has been taken: thus, reset write mode 0! }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
JMP @LowerLeftTileDone
@mode0e:
OUT DX,AX {choose plane from which to read}
PUSH AX {and preserve it for later}
MOV DX,3C4h
MOV AX,0102h {choose write plane 0 }
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane0_row = (16+3-leftcut) DIV 4 }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane0_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane0_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {CX = save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile1d:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile1d
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0202h {choose write plane 1 }
OUT DX,AX
MOV DX,3CEh {next read plane: }
POP AX
INC AH
AND AH,3 {increment by 1 MOD 4}
JNE @nowrap1e
INC SI {plane 0 follows after plane 3 again, but}
{the source address has increased by 1 byte }
@nowrap1e:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+2-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane1_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane1_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane1_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {CX = save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile2d:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile2d
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0402h {choose write plane 2 }
OUT DX,AX
MOV DX,3CEh {next read plane: }
POP AX
INC AH
AND AH,3 {increment by 1 MOD 4}
JNE @nowrap2e
INC SI {plane 0 follows after plane 3 again, but}
{the source address has increased by 1 byte }
@nowrap2e:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+1-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane2_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane2_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane2_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {CX = save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile3d:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile3d
POP CX
POP BP
POP DI
POP SI
MOV DX,3C4h
MOV AX,0802h {choose write plane 3 }
OUT DX,AX
MOV DX,3CEh {next read plane: }
POP AX
INC AH
AND AH,3 {increment by 1 MOD 4}
JNE @nowrap3e
INC SI {plane 0 follows after plane 3 again, but}
{the source address has increased by 1 byte }
@nowrap3e:
OUT DX,AX
{do not push value again!}
DEC BP {BP:=16-leftcut}
@lastplane5:
SHR BP,1 {use BP directly }
SHR BP,1 {BP:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
MOV BX,CX {BX:=row counter }
@eineZeile4d:
MOV CX,BP {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BX
JNZ @eineZeile4d
@LowerLeftTileDone:
POP BP
MOV AX,SEG @Data {restore DS }
MOV DS,AX
@label2:
MOV AX,xpix
MOV x,AX
{Now the lower tiles, which are not partially cut at their left or right:}
@repeat4:
MOV CL,offscreenFlag
XOR SI,SI
DEC CL
JZ @go8
MOV AX,xt
OR AX,AX
JS @go8
CMP AX,XTiles
JAE @go8
MOV SI,actIndex
@go8:
{PROCEDURE DrawLowerTile(x,y,bottomcut:INTEGER; index:WORD);}
{ in: (x,y) = upper left corner of the tile to be drawn}
{ bottomcut = number of bottom tile-rows to be cut off }
{ SI = index = tile number}
{out: tile has been drawn on actual page PAGEADR }
{rem: tile has been clipped at its bottom accordingly}
{ bottomcut must lie in the range 0..15, 16 is not allowed!}
{ bottomcut could also be computed by: }
{ bottomcut:=y+15-ymax}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2 }
RCR AL,1 {therefore: set AH:=tile and afterwards, shift AX }
SHR AH,1 {2 bits to the right!}
RCR AL,1
MOV SI,AX {That's also the offset part of the source ad. in page3}
MOV DI,y {the first destination addr. is DI:=y*LINESIZE+(x div 4)}
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,x
MOV BX,AX {have a copy of X in BX }
SHR AX,1
SHR AX,1
ADD DI,AX
MOV CX,16
SUB CX,bottomcut {CX:=number of rows to draw = 16-bottomcut }
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,CX
SHL BP,1
SHL BP,1 {BP := (rows to draw)*4 }
MOV ES,PAGEADR {(segment part of) dest. address is active page}
MOV DS,SCROLLADR {(segment part of) source addr. is page SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3 {BX:=1st write plane (1st READ plane=0!) }
JNE @mode0f {only if x mod 4=0 is it possible to use WriteMode1 }
{--- short-cut possible, using write mode1 }
MOV AH,0Fh {work on all 4 planes simultaneously }
OUT DX,AX
MOV AX,4105h
MOV DX,3CEh
OUT DX,AX {choose write mode 1}
MOV BX,CX {BX:=CX=rows to draw }
MOV AX,LINESIZE-4
MOV CX,BX {CX:=number of rows to draw }
@eineZeile4e1:
MOVSB {no "MOVSW", as we use write mode1}
MOVSB
MOVSB
MOVSB
ADD DI,AX {set DI to next row }
LOOP @eineZeile4e1
{--- short cut has been taken: thus, reset write mode 0! }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
JMP @LowerTileDone
@mode0f:
MOV AH,CS:[OFFSET CS_TranslateTab+BX]
OUT DX,AX
PUSH AX {save actual write plane }
MOV DX,3CEh
MOV AX,0004h {choose plane 0 for reading }
OUT DX,AX
MOV AX,LINESIZE-4 {adjustment for row addresses }
MOV BX,CX {save number of rows to draw into BX }
@eineZeile1e:
MOVSW
MOVSW
ADD DI,AX {set DI to next row }
LOOP @eineZeile1e
MOV AX,0104h {DX=3CEh -> choose read plane 1}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1f
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap1f:
OUT DX,AX
PUSH AX
SHL BX,1 {reset DI to the source address: }
SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)* }
SHR BX,1 {LINESIZE (N.B.: BX=number of rows to draw)}
SUB SI,BP {reset SI, too }
MOV AX,LINESIZE-4
MOV CX,BX {CX:=number of rows to draw }
@eineZeile2e:
MOVSW
MOVSW
ADD DI,AX {set DI to next row }
LOOP @eineZeile2e
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2f
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap2f:
OUT DX,AX {DX=3C4h -> set write plane }
PUSH AX
MOV DX,3CEh
MOV AX,0204h {set read plane 2 }
OUT DX,AX
SHL BX,1 {reset DI to the source address: }
SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)* }
SHR BX,1 {LINESIZE (N.B.: BX=number of rows to draw)}
SUB SI,BP {reset SI, too }
MOV AX,LINESIZE-4
MOV CX,BX {CX:=number of rows to draw }
@eineZeile3e:
MOVSW
MOVSW
ADD DI,AX {set DI to next row }
LOOP @eineZeile3e
MOV AX,0304h
OUT DX,AX {DX=3CEh -> choose read plane 3}
MOV DX,3C4h
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3f
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap3f:
OUT DX,AX {DX=3C4h -> set write plane }
SHL BX,1 {reset DI to the source address: }
SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)* }
SHR BX,1 {LINESIZE (N.B.: BX=number of rows to draw)}
SUB SI,BP {reset SI, too }
@lastPlane6:
MOV AX,LINESIZE-4
MOV CX,BX {CX:=number of rows to draw }
@eineZeile4e:
MOVSW
MOVSW
ADD DI,AX {set DI to next row }
LOOP @eineZeile4e
@LowerTileDone:
POP BP
MOV AX,SEG @Data {restore DS }
MOV DS,AX
INC xt
INC actIndex
MOV AX,x
ADD AX,16
MOV x,AX
CMP AX,XMAX+1-16
JBE @repeat4
{Now evtl. the lower right corner-tile remains to be drawn:}
CMP AX,XMAX+1
JE @do_raender
MOV CL,offscreenFlag
XOR SI,SI
DEC CL
JZ @go9
MOV AX,xt
OR AX,AX
JS @go9
CMP AX,XTiles
JAE @go9
MOV SI,actIndex
@go9:
{PROCEDURE DrawLowerRightTile(x,y,rightcut,bottomcut:INTEGER; index:WORD);}
{ in: (x,y) = upper left corner of the tile to be drawn}
{ rightcut = number of right tile-columns to be cut off }
{ bottomcut = dto., bottom}
{ bottomcut must lie in the range 0..15, 16 is not allowed}
{ bottomcut could also be computed by: }
{ bottomcut:=y+15-ymax}
{ SI = index = tile number}
{out: tile has been drawn on actual page PAGEADR }
{rem: tile has been clipped at its right and bottom accordingly}
{ rightcut must lie between 0..15, 16 is allowed (but senseless)! }
{ rightcut could also be computed (for x>xmax-16) by }
{ rightcut := x+15-xmax }
{ bottomcut must lie in the range 0..15, 16 is not allowed}
{ bottomcut could also be computed by: }
{ bottomcut:=y+15-ymax}
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2 }
RCR AL,1 {therefore: set AH:=tile and afterwards, shift AX }
SHR AH,1 {2 bits to the right!}
RCR AL,1
MOV SI,AX {That's also the offset part of the source ad. in page3}
MOV DI,y {the first destination addr. is DI:=y*LINESIZE+(x div 4)}
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,x
MOV BX,AX {place a copy of x in BX}
SHR AX,1
SHR AX,1
ADD DI,AX
MOV AX,rightcut
MOV CX,16
SUB CX,bottomcut {CX:=16-bottomcut = rows to draw }
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,16+3
SUB BP,AX {BP:=16+3-rightcut, because the number of bytes per row }
{of plane i is computed by (16+3-i-rightcut) SHR 2 }
MOV AH,AL {save rightcut to AH }
MOV ES,PAGEADR {(segment part of) dest. address is active page}
MOV DS,SCROLLADR {(segment part of) source addr. is page SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3
JNE @mode0g {only if x mod 4=0 _and_ rightcut mod 4=0, then we can }
AND AH,3 {use write mode 1! }
JNE @mode0g
{--- short-cut possible, using write mode1: DX=3C4h, BP=16+3-rightcut }
MOV AH,0Fh
OUT DX,AX {work on all 4 planes simultaneously }
MOV DX,3CEh
MOV AX,4105h {choose write mode 1}
OUT DX,AX
SUB BP,3 {set BP to proper size }
SHR BP,1 {use BP directly }
SHR BP,1 {BX:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
MOV BX,CX {BX:=row counter }
@eineZeile4g1:
MOV CX,BP {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BX
JNZ @eineZeile4g1
{--- short cut has been taken: thus, reset write mode 0! }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
JMP @LowerRightTileDone
@mode0g:
MOV AH,CS:[OFFSET CS_TranslateTab +BX]
OUT DX,AX {choose plane to write at }
PUSH AX {and preserve it for later}
MOV DX,3CEh
MOV AX,0004h {choose read plane 0}
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane0_row = (16+3-rightcut) DIV 4 }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane0_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane0_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {CX = save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile1f:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile1f
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0104h {choose read plane 1}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1g
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap1g:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+2-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane1_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane1_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane1_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {CX = save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile2f:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile2f
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0204h {choose read plane 2}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2g
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap2g:
OUT DX,AX
PUSH AX
DEC BP {BP:=16+1-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane2_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane2_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane2_row }
PUSH SI {save source- and destination address for other planes}
PUSH DI
PUSH BP {save BP }
PUSH CX {CX = save row counter }
MOV BP,CX {BP:=row counter }
@eineZeile3f:
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BP
JNZ @eineZeile3f
POP CX
POP BP
POP DI
POP SI
MOV DX,3CEh
MOV AX,0304h {choose read plane 3}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3g
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap3g:
OUT DX,AX
{do not push value again!}
DEC BP {BP:=16-rightcut}
@lastplane7:
SHR BP,1 {use BP directly }
SHR BP,1 {BX:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
MOV BX,CX {BX:=row counter }
@eineZeile4g:
MOV CX,BP {CX:=bytes_per_plane0_row }
REP MOVSB {move data of one row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
DEC BX
JNZ @eineZeile4g
@LowerRightTileDone:
POP BP
MOV AX,SEG @Data {restore DS }
MOV DS,AX
{Now work on the tiles at the left and/or right edge of the screen: }
@do_raender:
CMP leftcut,0 {or "rightcut": because 320 is dividable by 16, }
JE @ende {leftcut=0 is equivalent to rightcut=0 }
MOV AX,offsetXTiles
DEC AX
MOV xt,AX
MOV AX,ypix
MOV y,AX
INC tiles
MOV AX,offsetYTiles
MOV yt,AX
@repeat5:
MOV CL,1
OR AX,AX
JS @go10
CMP AX,YTiles
JAE @go10
DEC CL
@go10:
MOV offscreenFlag,CL
XOR SI,SI
DEC CL
JZ @go11
MOV AX,xt
OR AX,AX
JS @go11
CMP AX,XTiles
JAE @go11
MOV SI,RandIndex
@go11:
{PROCEDURE DrawLeftTile(y,leftcut:INTEGER; index:WORD);}
{ in: (0,y) = upper left corner of the tile to be drawn}
{ leftcut = number of left tile-columns to be cut off }
{ SI = index = tile number}
{out: tile has been drawn on actual page PAGEADR }
{rem: tile has been clipped at its left accordingly}
{ leftcut must lie in the range 0..15, 16 is allowed (but senseless) }
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2 }
RCR AL,1 {therefore: set AH:=tile and afterwards, shift AX }
SHR AH,1 {2 bits to the right!}
RCR AL,1
MOV SI,AX {That's also the offset part of the source ad. in page3}
MOV DI,y {first dest. addr. is DI:=y*LINESIZE+(0 div 4)=y*LINESIZE }
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,leftcut
MOV BX,AX {have a copy of leftcut in BX }
SHR AX,1
SHR AX,1
ADD SI,AX {increment SI by cutoff (=leftcut div 4) bytes }
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,16+3
SUB BP,BX {BP:=16+3-leftcut, because the number of bytes per row }
{of plane i is computed by (16+3-i-leftcut) SHR 2 }
MOV ES,PAGEADR {(segment part of) dest. address is active page}
MOV DS,SCROLLADR {(segment part of) source addr. is page SCROLLPAGE}
MOV DX,3CEh
AND BL,3
MOV AH,BL {AH:=leftcut mod 4}
MOV AL,4
JNE @mode0h {only if leftcut mod 4=0 is it possible to use WriteMode1 }
{--- short-cut possible, using write mode1: DX=3CEh, BP=16+3-leftcut }
{ BP being "3 units to big" doesn't matter, because this code will }
{ only be run if leftcut mod 4=0, thus 16+3-leftcut=...11b, the }
{ "11b" will be cut off while shifting right, anyway! }
MOV AX,4105h
OUT DX,AX {choose write mode 1}
MOV DX,3C4h
MOV AX,0F02h {work on all 4 planes simultaneously }
OUT DX,AX
SHR BP,1 {use BP directly }
SHR BP,1 {BP:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
MOV CX,BP {CX:=bytes_per_plane2_row }
REP MOVSB {1.row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
MOV CX,BP
REP MOVSB {2.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {3.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {4.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {5.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {6.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {7.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {8.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {9.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {10.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {11.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {12.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {13.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {14.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {15.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {16.row }
{--- short cut has been taken: thus, reset write mode 0! }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
JMP @LeftTileDone
@mode0h:
OUT DX,AX {choose plane from which to read}
PUSH AX {and preserve it for later}
MOV DX,3C4h
MOV AX,0102h {choose write plane 0 }
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane0_row = (16+3-leftcut) DIV 4 }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane0_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane0_row }
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {1.row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
MOV CX,BX
REP MOVSB {2.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.row }
ADD DI,AX
ADD SI,DX
MOV DX,3C4h
MOV AX,0202h {choose write plane 1 }
OUT DX,AX
MOV DX,3CEh {next read plane: }
POP AX
INC AH
AND AH,3 {increment by 1 MOD 4}
JNE @nowrap1h
INC SI {plane 0 follows after plane 3 again, but}
{the source address has increased by 1 byte }
@nowrap1h:
OUT DX,AX
PUSH AX
SUB DI,16*LINESIZE {reset DI and SI registers }
SUB SI,16*4
DEC BP {BP:=16+2-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane1_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane1_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane1_row }
MOV CX,BX {CX:=bytes_per_plane1_row }
REP MOVSB {1.row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
MOV CX,BX
REP MOVSB {2.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.row }
ADD DI,AX
ADD SI,DX
MOV DX,3C4h
MOV AX,0402h {choose write plane 2 }
OUT DX,AX
MOV DX,3CEh {next read plane: }
POP AX
INC AH
AND AH,3 {increment by 1 MOD 4}
JNE @nowrap2h
INC SI {plane 0 follows after plane 3 again, but}
{the source address has increased by 1 byte }
@nowrap2h:
OUT DX,AX
PUSH AX
SUB DI,16*LINESIZE {reset DI and SI registers }
SUB SI,16*4
DEC BP {BP:=16+1-leftcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane2_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane2_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane2_row }
MOV CX,BX {CX:=bytes_per_plane2_row }
REP MOVSB {1.row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
MOV CX,BX
REP MOVSB {2.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.row }
ADD DI,AX
ADD SI,DX
MOV DX,3C4h
MOV AX,0802h {choose write plane 3 }
OUT DX,AX
MOV DX,3CEh {next read plane: }
POP AX
INC AH
AND AH,3 {increment by 1 MOD 4}
JNE @nowrap3h
INC SI {plane 0 follows after plane 3 again, but}
{the source address has increased by 1 byte }
@nowrap3h:
OUT DX,AX
{do not push value again!}
SUB DI,16*LINESIZE {reset DI and SI registers }
SUB SI,16*4
DEC BP {BP:=16-leftcut}
@lastplane8:
SHR BP,1 {use BP directly }
SHR BP,1 {BP:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
MOV CX,BP {CX:=bytes_per_plane2_row }
REP MOVSB {1.row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
MOV CX,BP
REP MOVSB {2.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {3.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {4.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {5.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {6.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {7.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {8.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {9.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {10.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {11.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {12.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {13.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {14.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {15.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {16.row }
@LeftTileDone:
POP BP
MOV AX,SEG @Data {restore DS }
MOV DS,AX
MOV CL,offscreenFlag
XOR SI,SI
DEC CL
JZ @go12
MOV AX,xt
MOV DX,tiles
ADD AX,DX
JS @go12
CMP AX,XTiles
JAE @go12
MOV SI,RandIndex
ADD SI,DX
@go12:
{PROCEDURE DrawRightTile(x,y,rightcut:INTEGER; index:WORD);}
{ in: (x,y) = upper left corner of the tile to be drawn}
{ rightcut = number of right tile-columns to be cut off }
{ SI = index = tile number}
{out: tile has been drawn on actual page PAGEADR }
{rem: tile has been clipped at its right accordingly}
{ rightcut must lie between 0..15, 16 is allowed (but senseless)! }
{ rightcut could also be computed (for x>xmax-16) by }
{ rightcut := x+15-xmax }
MOV AH,[OFFSET BackTile +SI] {AH:=BackTile[index]}
XOR AL,AL
SHR AH,1 {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2 }
RCR AL,1 {therefore: set AH:=tile and afterwards, shift AX }
SHR AH,1 {2 bits to the right!}
RCR AL,1
MOV SI,AX {That's also the offset part of the source ad. in page3}
MOV DI,y {the first destination addr. is DI:=y*LINESIZE+(x div 4)}
SHL DI,1
MOV DI,CS:[OFFSET GADR + DI]
MOV AX,x
MOV BX,AX {place a copy of x in BX}
SHR AX,1
SHR AX,1
ADD DI,AX
MOV CX,rightcut
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,16+3
SUB BP,CX {BP:=16+3-rightcut, because the number of bytes per row }
{of plane i is computed by (16+3-i-rightcut) SHR 2 }
MOV ES,PAGEADR {(segment part of) dest. address is active page}
MOV DS,SCROLLADR {(segment part of) source addr. is page SCROLLPAGE}
MOV DX,3C4h
MOV AL,2
AND BX,3
JNE @mode0i {only if x mod 4=0 _and_ rightcut mod 4=0, then we can }
AND CX,3 {use write mode 1! }
JNE @mode0i
{--- short-cut possible, using write mode1: DX=3C4h, BP=16+3-rightcut }
MOV AH,0Fh
OUT DX,AX {work on all 4 planes simultaneously }
MOV DX,3CEh
MOV AX,4105h {choose write mode 1}
OUT DX,AX
SUB BP,3 {set BP to proper size }
SHR BP,1 {use BP directly }
SHR BP,1 {BP:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
MOV CX,BP {CX:=bytes_per_plane2_row }
REP MOVSB {1.row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
MOV CX,BP
REP MOVSB {2.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {3.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {4.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {5.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {6.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {7.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {8.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {9.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {10.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {11.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {12.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {13.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {14.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {15.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {16.row }
{--- short cut has been taken: thus, reset write mode 0! }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
{---}
JMP @RightTileDone
@mode0i:
MOV AH,CS:[OFFSET CS_TranslateTab +BX]
OUT DX,AX {choose plane to write at }
PUSH AX {and preserve it for later}
MOV DX,3CEh
MOV AX,0004h {choose read plane 0}
OUT DX,AX
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane0_row = (16+3-rightcut) DIV 4 }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane0_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane0_row }
MOV CX,BX {CX:=bytes_per_plane0_row }
REP MOVSB {1.row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
MOV CX,BX
REP MOVSB {2.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.row }
ADD DI,AX
ADD SI,DX
MOV DX,3CEh
MOV AX,0104h {choose read plane 1}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1i
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap1i:
OUT DX,AX
PUSH AX
SUB DI,16*LINESIZE {reset DI and SI registers }
SUB SI,16*4
DEC BP {BP:=16+2-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane1_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane1_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane1_row }
MOV CX,BX {CX:=bytes_per_plane1_row }
REP MOVSB {1.row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
MOV CX,BX
REP MOVSB {2.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.row }
ADD DI,AX
ADD SI,DX
MOV DX,3CEh
MOV AX,0204h {choose read plane 2}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2i
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap2i:
OUT DX,AX
PUSH AX
SUB DI,16*LINESIZE {reset DI and SI registers }
SUB SI,16*4
DEC BP {BP:=16+1-rightcut}
MOV BX,BP
SHR BX,1
SHR BX,1 {BX:=bytes_per_plane2_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BX { := LINESIZE-bytes_per_plane2_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BX { := 4-bytes_per_plane2_row }
MOV CX,BX {CX:=bytes_per_plane2_row }
REP MOVSB {1.row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
MOV CX,BX
REP MOVSB {2.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {3.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {4.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {5.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {6.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {7.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {8.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {9.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {10.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {11.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {12.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {13.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {14.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {15.row }
ADD DI,AX
ADD SI,DX
MOV CX,BX
REP MOVSB {16.row }
ADD DI,AX
ADD SI,DX
MOV DX,3CEh
MOV AX,0304h {choose read plane 3}
OUT DX,AX
MOV DX,3C4h {next write plane: }
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3i
MOV AH,1 {plane 0 follows after plane 3 again, but}
INC DI {the destination address has increased by 1 byte}
@nowrap3i:
OUT DX,AX
{do not push value again!}
SUB DI,16*LINESIZE {reset DI and SI registers }
SUB SI,16*4
DEC BP {BP:=16-rightcut}
@lastplane9:
SHR BP,1 {use BP directly }
SHR BP,1 {BP:=bytes_per_plane3_row }
MOV AX,LINESIZE {adjustment for row target addresses }
SUB AX,BP { := LINESIZE-bytes_per_plane3_row }
MOV DX,4 {adjustment for source addresses of the tiles}
SUB DX,BP { := 4-bytes_per_plane3_row }
MOV CX,BP {CX:=bytes_per_plane2_row }
REP MOVSB {1.row }
ADD DI,AX {set DI to next destination row }
ADD SI,DX {set SI to next tile's source addr. }
MOV CX,BP
REP MOVSB {2.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {3.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {4.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {5.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {6.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {7.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {8.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {9.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {10.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {11.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {12.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {13.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {14.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {15.row }
ADD DI,AX
ADD SI,DX
MOV CX,BP
REP MOVSB {16.row }
@RightTileDone:
POP BP
MOV AX,SEG @Data {restore DS }
MOV DS,AX
MOV AX,RandIndex
ADD AX,XTiles
MOV RandIndex,AX
MOV AX,yt
INC AX
MOV yt,AX
MOV DX,y
ADD DX,16
MOV y,DX
CMP DX,YMAX+1-16
JBE @repeat5
@ende:
{------- starting here: put sprites on actual graphic page}
@Sprites_zeichnen:
MOV SI,NMAX*2
PUSH BP {pop BP in the end! }
@zeichne:
{DS = normal data segment, ES = graphic page segment, }
{SI = spritepositionnumber*2 }
@SZeich:
MOV BX,[SI + OFFSET SpriteN] {BX=SpriteN[?]=Spriteloadnumber}
SHL BX,1 {BX = sprite load number*2}
{Now: compute "SpriteN[?]:=SpriteN[NextSprite[?]]" }
MOV BX,[BX + OFFSET NextSprite] {AX=NextSprite[SpriteN[?]]}
MOV [SI + OFFSET SpriteN],BX {use it as new SpriteN[?] value }
SHL BX,1
JNZ @aktiv
JMP @noSprite
@aktiv:
PUSH SI {save spritepositionnumber*2 }
MOV DX,[SI + OFFSET SpriteX] {if SpriteX>xmax then skip_sprite }
SUB DX,StartVirtualX {virtual -> absolute coordinates }
CMP DX,XMAX
JLE @L0
@ToSprite_fertig: {jump-rail to @Sprite_fertig }
JMP @Sprite_fertig
@L0:
MOV CS:WORD PTR @akt_SpriteX+1,DX
MOV DI,[SI + OFFSET SpriteY] {DI = SpriteY_virtual }
SUB DI,StartVirtualY {DI = SpriteY (absolute!)}
MOV DS,[BX + OFFSET SPRITEAD] {!!!DS = ^sprite data !!!}
MOV AX,[Breite] {AX = width in groups of 4 }
MOV CS:WORD PTR @max_Breite+1,AX
MOV SI,AX {SI = dto.}
SHL AX,1
SHL AX,1 {AX = max_width_in_points }
ADD AX,DX {AX = max_width_in_points+SpriteX }
JS @ToSprite_fertig
MOV BX,DI {if SpriteY>=0 then starty:=+SpriteY}
NEG DI { else starty:=-SpriteY}
MOV BP,DI
JG @Top_cut
XOR DI,DI
@Top_cut: {DI = starty, BP = -SpriteY}
MOV AX,[Hoehe] {AX = height (in rows) }
CMP DI,AX {if starty>=height then skip_sprite}
JGE @ToSprite_fertig
ADD BP,YMAX {BP = -SpriteY+ymax}
JL @ToSprite_fertig {(a bit lax:) }
CMP AX,BP {if height+SpriteY>ymax }
JG @To_then { then [ endy:=199-SpriteY }
DEC AX { if endy<0 then skip_sprite ] }
MOV BP,AX { else endy:=height-1 }
{BP = endy, SI=[@max_Breite+1] = max_width_in_groups of 4, }
{DI = starty, BX = SpriteY, DX=[@akt_SpriteX+1] = SpriteX, }
{DS = ^sprite data, ES = ^graphic page}
@To_then:
MOV AX,BP
SUB BP,DI
SHL BP,1
MOV [End_min_Start],BP {= (endy-starty)*2 =Yaktuell*2}
ADD BX,AX
SHL BX,1
MOV BX,CS:[OFFSET gadr + BX] {BX=zeilenadr:=(endy+SpriteY)*LINESIZE}
MOV [zeilenadr],BX {store it in [zeilenadr], too }
MOV BP,DX
MUL SI {AX=endy*max_width_in_groups_of_4=yoffset}
MOV [yoffset_],AX {store it in [yoffset_], too}
SHL DI,1 {DI = starty*2}
MOV CS:WORD PTR @Starty_2+1,DI {store it in [@Starty_2+1], too }
{Now: look at sprite's mode byte and determine if an other than the }
{momentary active sprite display method is needed; if so, then copy }
{in the right one! }
{used registers: AX and SI }
MOV AL,[Modus] {get mode byte of the sprite}
XOR AH,AH
SHL AX,1
MOV SI,AX
MOV SI,CS:[OFFSET Adressen +SI] {get pointer to according routine }
MOV AX,CS:[SI]
CMP AX,CS:[WORD PTR @Patch1] {is this routine already active? }
JE @no_newcode {yes, nothing to do}
PUSH DS {no, copy routine to all places }
PUSH CS {where it will be needed}
POP DS
MOV [WORD PTR @Patch1],AX
MOV [WORD PTR @Patch2],AX
MOV [WORD PTR @Patch3],AX
MOV [WORD PTR @Patch4],AX
INC SI
INC SI
LODSW
MOV [WORD PTR @Patch1+2],AX
MOV [WORD PTR @Patch2+2],AX
MOV [WORD PTR @Patch3+2],AX
MOV [WORD PTR @Patch4+2],AX
LODSW
MOV [WORD PTR @Patch1+4],AX
MOV [WORD PTR @Patch2+4],AX
MOV [WORD PTR @Patch3+4],AX
MOV [WORD PTR @Patch4+4],AX
LODSW
MOV [WORD PTR @Patch1+6],AX
MOV [WORD PTR @Patch2+6],AX
MOV [WORD PTR @Patch3+6],AX
MOV [WORD PTR @Patch4+6],AX
LODSW
MOV [WORD PTR @Patch1+8],AX
MOV [WORD PTR @Patch2+8],AX
MOV [WORD PTR @Patch3+8],AX
MOV [WORD PTR @Patch4+8],AX
LODSW
MOV [WORD PTR @Patch1+10],AX
MOV [WORD PTR @Patch2+10],AX
MOV [WORD PTR @Patch3+10],AX
MOV [WORD PTR @Patch4+10],AX
LODSW
MOV [WORD PTR @Patch1+12],AX
MOV [WORD PTR @Patch2+12],AX
MOV [WORD PTR @Patch3+12],AX
MOV [WORD PTR @Patch4+12],AX
LODSW
MOV [WORD PTR @Patch1+14],AX
MOV [WORD PTR @Patch2+14],AX
MOV [WORD PTR @Patch3+14],AX
MOV [WORD PTR @Patch4+14],AX
POP DS {restore DS }
@no_newcode:
{(AX=)[yoffset_] = yoffset }
{ BX=[zeilenadr] = (endy+SpriteY)*LINESIZE}
{ DI=[@Starty_2+1] = starty*2}
{(SI=[@max_Breite+1] = max_width_in_groups_of_4) }
{ BP = SpriteX}
{ DS = ^sprite data}
{ ES = ^graphic page}
{ [end_min_start] = (endy-starty)*2 =Yaktuell*2}
{ [@max_Breite+1] = max_width_in_groups_of_4 }
{ [@akt_SpriteX+1] = SpriteX}
@eine_Zeile:
MOV SI,[end_min_start] {SI = Yactual*2 }
ADD SI,DI {startx:=sprite[WORD PTR sprite[L]+ }
MOV DI,SI { (Yactual+starty)*2] }
ADD SI,[Left]
MOV SI,[SI] {SI = startx, DI = (Yaktuell+starty)*2}
MOV AX,BP
ADD AX,SI {AX=ScreenStartX:=SpriteX+startx }
CMP AX,XMAX {if ScreenStartX>xmax then skip_zeile }
JG @ToZeile_fertig
MOV CX,SI {CX=startx}
OR AX,AX {lecutoff_in_points:=startx }
JGE @L1 {if ScreenStartX<0 then }
SUB SI,AX { [dec(startx,ScreenStartX) }
XOR AX,AX { ScreenStartX:=0 }
MOV CX,BP { lecutoff_in_points:=-SpriteX] }
NEG CX
@L1: {CX=[licutoff_]= licutoff_in_points, }
MOV [licutoff_],CX {SI = startx, AX = ScreenStartX }
ADD DI,[Right]
MOV DI,[DI] {DI=endx:=sprite[WORD PTR sprite[R]+ }
{ (Yactual+starty)*2] }
MOV DX,BP
NEG DX {DX = -SpriteX }
MOV BP,DI
SUB BP,SI {BP = endx-startx }
SUB DX,DI
ADD DX,XMAX {DX=overhang :=xmax-(SpriteX+endx) }
JNS @kein_Ueberhang_rechts
ADD BP,DX
@kein_Ueberhang_rechts: {BP = visible width of this row -1 }
OR BP,BP
JNS @L6
@ToZeile_fertig:
JMP @Zeile_fertig {if width<=0 then skip_zeile }
@L6:
ADD BP,4
{ AX = ScreenStartX }
{ BX=[zeilenadr] = (endy+SpriteY)*LINESIZE }
{ CX=[licutoff_] = lecutoff_in_points }
{(DX = (negative) overhang (if value<0) ) }
{(SI = startx) }
{(DI = endx) }
{ BP = width of this row in pixels +3 }
{ DS = ^sprite data}
{ ES = ^graphic page}
{ [@max_Breite+1] = max_width_in_groups_of_4) }
{ [end_min_start] = (endy-starty)*2 =Yaktuell*2}
{ [@Starty_2+1] = starty*2}
{ [@max_Breite+1] = max_width_in_groups_of_4, }
{ [@akt_SpriteX+1] = SpriteX}
MOV [bildx],AX {save ScreenStartX }
MOV DX,CX {DX = lecutoff_in_points }
MOV CX,BP
SHR CX,1
SHR CX,1 {CX = width DIV 4 }
JCXZ @Plane1
{SI=source pointer:=sprite[WORD PTR (lecutoff_in_points+0 AND 3)*2 }
{ +(lecutoff_in_points+0) DIV 4 +yoffset }
MOV SI,DX
AND SI,3
SHL SI,1 {SI = ((lecutoff_in_points+0) AND 3)*2 }
MOV SI,[SI]
MOV DI,DX
SHR DI,1
SHR DI,1
ADD SI,DI
ADD SI,[yoffset_] {SI = sprite[WORD PTR (licutoff_...)] }
{ +(lecutoff_in_points+i) DIV 4 }
{ +yoffset }
{DI=dest.ptr:=(ScreenStartX+0) DIV 4 +zeilenadr }
MOV DI,AX {DI = ScreenStartX }
SHR DI,1
SHR DI,1
ADD DI,BX
MOV BL,AL
AND BX,3 {BX = (ScreenStartX+i) AND 3 }
MOV AH,Translate[BX] {AH = 1,2,4,8 for BX=0,1,2,3 }
MOV AL,2
MOV DX,3C4h
OUT DX,AX {choose plane }
XCHG BX,DI
{copy CX bytes from DS:SI to ES:BX }
{this is the address where the data transfer routine will be patched!}
@Patch1:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Plane1:
MOV DX,[bildx]
INC DX {DX = ScreenStartX+1 }
MOV BX,DX
SHR BX,1
SHR BX,1 {BX=dest. ptr :=(ScreenStartX+1) }
ADD BX,[zeilenadr] { DIV 4 +zeilenadr }
MOV CX,BP
DEC CX {CX = width of this line +3 -1 }
SHR CX,1
SHR CX,1 {CX = bytes_to_move for i=1 }
JCXZ @Plane2
MOV DI,[licutoff_]
INC DI {DI = (lecutoff_in_points+1) }
MOV SI,DI
AND SI,3
SHL SI,1 {SI = ((lecutoff_in_points+1) AND 3)*2 }
MOV SI,[SI] {SI = sprite[WORD PTR licutoff_...] }
SHR DI,1 { +(lecutoff_in_points+1) DIV 4 }
SHR DI,1 { +yoffset }
ADD SI,DI
ADD SI,[yoffset_] {SI = source pointer, }
{DI = (lecutoff_in_points+1) DIV 4 }
MOV DI,DX {DI = ScreenStartX+1 }
AND DI,3 {DI = (ScreenStartX+1) AND 3 }
MOV AH,Translate[DI] {load mask for port-access }
MOV AL,2
MOV DX,3C4h {select plane }
OUT DX,AX
{this is the address where the data transfer routine will be patched!}
@Patch2:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Plane2:
MOV DX,[bildx]
ADD DX,2
MOV BX,DX
SHR BX,1
SHR BX,1
ADD BX,[zeilenadr]
MOV CX,BP
SUB CX,2
SHR CX,1
SHR CX,1
JCXZ @Plane3
MOV DI,[licutoff_]
ADD DI,2
MOV SI,DI
AND SI,3
SHL SI,1
MOV SI,[SI]
SHR DI,1
SHR DI,1
ADD SI,DI
ADD SI,[yoffset_]
MOV DI,DX {DI = ScreenStartX+2 }
AND DI,3 {DI = (ScreenStartX+1) AND 3 }
MOV AH,Translate[DI]
MOV AL,2
MOV DX,3C4h
OUT DX,AX
{this is the address where the data transfer routine will be patched!}
@Patch3:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Plane3:
MOV DX,[bildx]
ADD DX,3
MOV BX,DX
SHR BX,1
SHR BX,1
ADD BX,[zeilenadr]
MOV CX,BP
SUB CX,3
SHR CX,1
SHR CX,1
JCXZ @Zeile_fertig
MOV DI,[licutoff_]
ADD DI,3
MOV SI,DI
AND SI,3
SHL SI,1
MOV SI,[SI]
SHR DI,1
SHR DI,1
ADD SI,DI
ADD SI,[yoffset_]
MOV DI,DX {DI = ScreenStartX+3 }
AND DI,3 {DI = (ScreenStartX+1) AND 3 }
MOV AH,Translate[DI]
MOV AL,2
MOV DX,3C4h
OUT DX,AX
{this is the address where the data transfer routine will be patched!}
@Patch4:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Zeile_fertig:
MOV AX,[yoffset_]
@max_Breite:
SUB AX,1234
MOV [yoffset_],AX
MOV BX,[zeilenadr]
SUB BX,LINESIZE
MOV [zeilenadr],BX
SUB WORD PTR [end_min_start],2
JS @Sprite_fertig
@Starty_2:
MOV DI,1234
@akt_SpriteX:
MOV BP,1234
JMP @eine_Zeile
@Sprite_fertig:
POP SI
MOV AX,SEG @Data
MOV DS,AX
@noSprite:
DEC SI
DEC SI
JS @fertig
JMP @zeichne
@fertig:
POP BP
{The graphic page is now ready to be displayed: }
cli
mov dx,StatusReg
{Wait for "display enable"=0 (that is: active), so that page flipping }
{for HB/LB takes place while displaying the same page:}
@WaitNotHSyncLoop:
in al,dx
and al,1
jnz @WaitNotHSyncLoop
@WaitHSyncLoop:
in al,dx
and al,1
jz @WaitHSyncLoop
MOV DX,CRTAddress {CRT-Controller}
MOV AL,$0D {LB-startaddress-register}
OUT DX,AL
INC DX
{ realize "AX:=Offset_Adr[Page]": }
MOV SI,PAGE {page value *2 (word-sized entries!)}
MOV BX,SI {save page value to BX! }
SHL SI,1 {add start address of array to that }
ADD SI,OFFSET Offset_Adr-StartIndex*2 {evtl. correct displacement }
LODSW {and fetch value}
OUT DX,AL {set LB of new starting address }
DEC DX
MOV AL,$0C
OUT DX,AL
INC DX
MOV AL,AH {set HB of new starting address }
OUT DX,AL
STI
NEG BX {new PAGE-value := 1-old PAGE-value, that is: }
ADD BX,1 {IF PAGE=0 THEN PAGE:=1 ELSE (PAGE=1) PAGE:=0 }
MOV PAGE,BX
SHL BX,1 {new PAGEADR-value := Segment_Adr[PAGE] }
ADD BX,OFFSET Segment_Adr-StartIndex*2
MOV AX,[BX]
MOV PAGEADR,AX
{Check whether the preset (min.) cycle time has passed: }
@L10:
MOV AL,TimeFlag {bit 7 = 0/1 for delay completed/not yet completed }
AND AL,$80
JE @L10
{start time control mechanism for next cycle: }
MOV AL,IsAT {is this an AT or better? ($0/$80=yes/no)}
OR AL,AL {timing mechanism only works on AT/386 }
JNE @L11 {otherwise: no timing mechanism! }
MOV TimeFlag,AL {use AL=0 as starting value, too }
MOV DX,WORD PTR CycleTime {store min. time for 1 cycle (micro- }
MOV CX,WORD PTR CycleTime+2 {seconds: CX=HIGH-Word, DX=LOW-Word }
MOV BX,OFFSET TimeFlag {ES:BX=pointer to TimeFlag, bit 7=0/1}
MOV AX,DS {for: delay still lasts/has ended }
MOV ES,AX
MOV AX,8300h {start time control mechanism}
INT 15h
@L11:
END
END;
FUNCTION LoadSprite(name:String; number:WORD):WORD;
{ in: name = name of the sprite file to load (type: "*.COD" / "*.LIB") }
{ number = number for the first sprite of this file }
{out: number of the sprites read in from the file (0 = error occured) }
{rem: The routine automatically detects whether the specified file con- }
{ tains a single sprite or a complete sprite library and loads all }
{ sprite data into the heap, in a way such that the address always }
{ lies on a segment (=paragraph) boundary. These starting addresses }
{ are then stored in table SPRITEAD[number]; if more than one sprite }
{ has been loaded, they will be stored with consecutive numbers, that }
{ is, number+i }
LABEL quit_loop;
VAR p1,p2:Pointer;
len:LONGINT;
f:File;
count,Kopf:WORD;
Header:SpriteHeader;
BEGIN
count:=0; {number of sprites already read in }
Kopf:=SizeOf(SpriteHeader);
assign(f,name);
{$I-} reset(f,1); {$I+}
if (ioresult<>0)
THEN BEGIN {File doesn't exist or at least not with that path }
Error:=Err_FileIO;
loadSprite:=0; exit
END;
len:=filesize(f); {determine file length}
if (maxavail<len+16)
THEN BEGIN
Error:=Err_NotEnoughMemory;
goto quit_loop;
END;
if (number=0) or (number>LoadMAX)
THEN BEGIN
Error:=Err_InvalidSpritenumber;
goto quit_loop;
END;
WHILE NOT EOF(f) DO
BEGIN
{first, read in the sprite header: }
{$I-} {load sprite header vià BLOCKREAD into the heap }
blockread(f,Header,Kopf);
{$I+}
IF (ioresult<>0)
THEN BEGIN
Error:=Err_FileIO;
goto quit_loop;
END;
IF (Header.Kennung[1]<>'K') or (Header.Kennung[2]<>'R')
THEN BEGIN
Error:=Err_NoSprite;
goto quit_loop;
END;
IF (Header.SpriteLength>MaxAvail+15) {enough space left? }
THEN BEGIN
Error:=Err_NotEnoughMemory;
goto quit_loop;
END;
{Now read in the real sprite data: }
getmem(p1,Header.SpriteLength+15); {get enough space }
IF (LONGINT(p1) mod 16)=0
THEN p2:=p1 {make p2 fall on segment boundary}
ELSE LONGINT(p2):=LONGINT(p1) + (16-LONGINT(p1) mod 16);
MOVE(Header,p2^,Kopf); {store sprite header to heap }
LONGINT(p1):=LONGINT(p2)+Kopf; {points exactly behind the header}
{$I-} {load the "rest" of the sprite }
blockread(f,p1^,Header.SpriteLength-Kopf);
{$I+}
IF (ioresult<>0)
THEN BEGIN
Error:=Err_FileIO;
goto quit_loop;
END;
{assign it to the sprite number:}
spritead[number+count]:=(longint(p2) shr 16)
+(longint(p2) and 65535) shr 4;
INC(count);
END;
quit_loop: ;
close(f);
loadSprite:=count
END;
FUNCTION LoadTile(name:STRING; number:BYTE):WORD;
{ in: name = name of the sprite file to load (type: "*.COD" / "*.LIB") }
{ number = 0..255 = tile number for the file's first sprite }
{out: number of the tiles read in from the file (0 = error occured) }
{rem: The routine automatically detects whether the specified file con- }
{ tains a single sprite or a complete sprite library and loads all }
{ sprites, splits them into tiles and stores them into the 4th graphic }
{ page, starting with the given number "number" }
{ Because a tile consists of 16x16 points, the sprites must be a mul- }
{ tiple of 16 points in each direction (x _and_ y) }
{ If the file contains more than one tile, they will be loaded by row,}
{ each row from left to right }
LABEL quit_loop;
TYPE split=RECORD loword,hiword:WORD END;
VAR p1:Pointer;
len,ad,p:LONGINT;
f:File;
count,Kopf,ZielOfs,step,yoffset:WORD;
pSeg,pOfs:ARRAY[0..3] OF WORD;
Breite_in_Tiles,Hoehe_in_Tiles,x,y,i,zeilen:BYTE;
Header:SpriteHeader;
BEGIN
count:=0; {number of sprites already read in }
Kopf:=SizeOf(SpriteHeader);
assign(f,name);
{$I-} reset(f,1); {$I+}
if (ioresult<>0)
THEN BEGIN {File doesn't exist or at least not with that path }
Error:=Err_FileIO;
LoadTile:=0; exit
END;
len:=filesize(f); {determine file length}
if (maxavail<len+16)
THEN BEGIN
Error:=Err_NotEnoughMemory;
goto quit_loop;
END;
WHILE NOT EOF(f) DO
BEGIN
{first, read in the sprite header: }
{$I-} {load sprite header vià BLOCKREAD into the heap }
blockread(f,Header,Kopf);
{$I+}
IF (ioresult<>0)
THEN BEGIN
Error:=Err_FileIO;
goto quit_loop;
END;
IF (Header.Kennung[1]<>'K') or (Header.Kennung[2]<>'R')
THEN BEGIN
Error:=Err_NoTile; {or Err_NoSprite! }
goto quit_loop
END;
IF (Header.Breite_in_4er_Gruppen MOD 4<>0) OR
(Header.Hoehe_in_Zeilen MOD 16<>0) {size a multiple of 16? }
THEN BEGIN
Error:=Err_NoTile;
goto quit_loop
END
ELSE BEGIN {yes, get number of tiles in that sprite file }
Breite_in_Tiles:=Header.Breite_in_4er_Gruppen SHR 2;
Hoehe_in_Tiles :=Header.Hoehe_in_Zeilen SHR 4;
step:=Breite_in_Tiles*4; {"step" needed for correct addressing}
END;
IF (Header.SpriteLength>MaxAvail) {enough space left? }
THEN BEGIN
Error:=Err_NotEnoughMemory;
goto quit_loop;
END;
{Now read in the real sprite data: }
getmem(p1,Header.SpriteLength); {get enough space }
{$I-} {load the "rest" of the sprite }
blockread(f,p1^,Header.SpriteLength-Kopf);
{$I+}
IF (ioresult<>0)
THEN BEGIN
Error:=Err_FileIO;
goto quit_loop;
END;
ad:=(LONGINT(split(p1).HiWord) SHL 4) + split(p1).LoWord - Kopf;
FOR i:=0 TO 3 DO
BEGIN
p:=ad+Header.Zeiger_auf_Plane[i]; pSeg[i]:=p SHR 4; pOfs[i]:=p AND $F;
END;
FOR y:=0 TO Pred(Hoehe_in_Tiles) DO
BEGIN
yoffset:=y*Breite_in_Tiles*16*(16 DIV 4);
FOR x:=0 TO Pred(Breite_in_Tiles) DO
BEGIN
IF count+number>255
THEN BEGIN
Error:=Err_InvalidTileNumber;
goto quit_loop
END;
ZielOfs:=(number+count) SHL 6;
FOR i:=0 TO 3 DO
BEGIN
PORTW[$3C4]:=(TranslateTab[i] SHL 8) + 2;
FOR zeilen:=0 TO 15 DO
BEGIN
move(mem[pSeg[i]:pOfs[i] + yoffset + zeilen*step + x*(16 DIV 4)],
mem[SCROLLADR:ZielOfs + zeilen*(16 DIV 4)],
16 DIV 4);
END;
END;
INC(count);
END;
END;
Dispose(p1);
END;
quit_loop: ;
close(f);
LoadTile:=count
END;
PROCEDURE SetBackgroundScrollRange(x1,y1,x2,y2:INTEGER);
{ in: (x1,y1) = upper left corner of the area (in virtual coord.)}
{ (x2,y2) = dto., lower right corner}
{out: (BackX1,BackY1), (BackX2,BackY2) = coord., rounded to 16'grid }
{ XTiles, YTiles = width and height of the chosen range in tiles }
{rem: The upper left corner will be torn to the upper left, the lower }
{ right corner to the lower right!}
{ Obviously, calling the routine only makes sense when using SCROLLING }
{ as background mode! }
BEGIN
BackX1:=x1 AND $FFF0; BackX2:=x2 OR $F;
BackY1:=y1 AND $FFF0; BackY2:=y2 OR $F;
xtiles:=succ(BackX2-BackX1) shr 4;
ytiles:=succ(BackY2-BackY1) shr 4;
IF (xtiles OR ytiles)<=0
THEN Error:=Err_InvalidCoordinates
ELSE IF xtiles*ytiles>MaxTiles
THEN Error:=Err_BackgroundToBig;
END;
PROCEDURE SetBackgroundMode(mode:BYTE);
{ in: mode = wanted background mode, STATIC or SCROLLING }
{out: Backgroundmode = set mode, STATIC/SCROLLING }
BEGIN
IF (mode<>STATIC) AND (mode<>SCROLLING)
THEN Error:=Err_InvalidMode
ELSE Backgroundmode:=mode
END;
PROCEDURE PutTile(x,y:INTEGER; TileNr:BYTE);
{ in: x,y = virtual coordinates where the tile shall be placed }
{ TileNr= number of the tile to be placed }
{out: - }
{rem: The point (x,y) first gets rounded to a grid with mesh 16 }
{ Calling this routine only makes sense when using SCROLLING}
{ as background mode! }
VAR index:WORD;
BEGIN
ASM
MOV AX,x {compute relative X-distance from left edge of }
SUB AX,BackX1 {the defined area and store it to "x", formula: }
SAR AX,1 { x:=((x AND $FFF0)-BackX1) DIV 16 (not: SHR 4)! }
SAR AX,1 {"AND $FFF0" isn't needed, because in BackX1, the }
SAR AX,1 {last hex-digit is $0! }
SAR AX,1
MOV x,AX
MOV AX,y {dto. for distance between the y-coordinate and }
SUB AX,BackY1 {top boundary: y:=((y AND $FFF0)-BackY1) DIV 16 }
SAR AX,1
SAR AX,1
SAR AX,1
SAR AX,1
MOV y,AX
END;
IF (x<0) OR (x>=XTiles) OR (y<0) OR (y>=YTiles)
THEN Error:=Err_InvalidCoordinates
ELSE BEGIN {a tile row has width XTiles, each tile consists of 16x16 points}
index:=y*XTiles+x; {to be exact: (x MOD XTiles)}
BackTile[Succ(index)]:=TileNr; {"succ", to hold free BackTile[0] }
END;
END;
PROCEDURE SetOffscreenTile(TileNr:BYTE);
{ in: TileNr= number of the tile to be placed }
{out: - }
{rem: all screen parts, which lie outside the window specified by }
{ SetBackgroundScrollRange will become the tile TileNr as }
{ pattern }
{ Calling this routine only makes sense when using SCROLLING }
{ as background mode! }
BEGIN
BackTile[0]:=TileNr
END;
PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
{ in: Sp = spriteLOADnumber, which mode byte shall be changed }
{out: M = method, which shall be used to display Sp from now on: }
{ Display_NORMAL, Display_FAST, Display_SHADOW or }
{ Display_SHADOWEXACT }
{rem: If the sprite doesn't exist yet (or the mode isn't allowed), }
{ nothing will happen at all! }
VAR ad:WORD;
BEGIN
ad:=SPRITEAD[Sp];
IF ad=0 THEN Error:=Err_InvalidSpriteNumber {sprite must already be loaded }
ELSE IF (M<Display_NORMAL) OR (M>Display_SHADOWEXACT)
THEN Error:=Err_InvalidMode {only these 4 modes are allowed }
ELSE MEM[ad:Modus]:=M
END;
FUNCTION GetModeByte(Sp:WORD):BYTE;
{ in: Sp = spriteLOADnumber, which mode byte shall be determined }
{out: actually set display method for sprite Sp: Display_NORMAL, }
{ Display_FAST, Display_SHADOW, Display_SHADOWEXACT or }
{ Display_UNKNOW, if the sprite hasn't been loaded, yet! }
VAR ad:WORD;
BEGIN
ad:=SPRITEAD[Sp];
IF (ad=0)
THEN GetModeByte:=Display_UNKNOWN {sprite not yet loaded }
ELSE GetModeByte:=MEM[SPRITEAD[Sp]:Modus]
END;
PROCEDURE FillPage(pa,color:Byte);
{ in: pa = the page which shall be filled (0..3) }
{ color = color with which to fill the page}
{out: graphic page "pa" has been filled with color "Color" }
{rem: Only "pa" values of 0,1 and BACKGNDPAGE make sense, but }
{ SCROLLPAGE is allowed, too }
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE) AND (pa<>SCROLLPAGE)
THEN Error:=Err_InvalidPageNumber
ELSE BEGIN
portw[$3C4]:=$0F02; {use MapMask register to select all 4 planes }
fillchar(MEM[Segment_Adr[pa]:0],PAGESIZE,Color)
END;
END;
PROCEDURE FillBackground(color:BYTE);
{ in: color = color for filling the background page BACKGNDPAGE }
{out: The graphic page BACKGNDPAGE has been filled with color "Color" }
{rem: Using the routine only makes sense when using background mode STATIC }
BEGIN
FillPage(BACKGNDPAGE,color)
END;
PROCEDURE GetBackgroundFromPage(pa:Byte);
{in : pa = 0 or 1 }
{out: - }
{rem: The background page BACKGNDPAGE has been filled with the con- }
{ tents of the supplied graphic page.}
{ Using the routine only makes sense when using background mode STATIC }
VAR p:POINTER;
BEGIN
IF (pa<>0) AND (pa<>1)
THEN Error:=Err_InvalidPageNumber
ELSE BEGIN
portw[$3c4]:=$0f02; {use MapMask register to select all 4 planes }
port[$3ce]:=5; {write mode 1 }
port[$3cf]:=port[$3cf] OR 1; {or directly :=$41}
p:=Ptr(Segment_Adr[pa],$0000);
MOVE(p^,MEM[BACKGNDADR:0],PAGESIZE);
portw[$3cf]:=port[$3cf] and $FC; {write mode 0 (or directly :=$40) }
END;
END;
PROCEDURE WritePage(name:STRING; pa:BYTE);
{ in: name = file name for the picture to store }
{ pa = page to store (0..2) }
{ PAGESIZE = size of one bitplane }
{ PICHeader= tag for picture file }
{out: - }
{rem: The picture at page "pa" has been stored (as bitmap) to file "name" }
{ This file has size 4*PAGESIZE+3 = 64003 bytes: 320x200 points , }
{ 1 byte plus length(PICHeader) as tag }
VAR f:FILE;
i,oldMode:BYTE;
fehler:BOOLEAN;
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber; exit
END;
{$I-}
Assign(f,name); fehler:=IOResult<>0;
Rewrite(f,1); fehler:=fehler OR (IOResult<>0);
BlockWrite(f,PICHeader[1],Length(PICHeader));
fehler:=fehler OR (IOResult<>0);
{$I+}
IF fehler
THEN BEGIN
{$I-} Close(f); {$I+}
Error:=Err_FileIO; exit
END;
port[$3ce]:=5; {save old read-/write mode }
oldMode:=port[$3cf];
port[$3cf]:=$40; {set read mode 0 }
FOR i:=0 TO 3 DO
BEGIN
portw[$3CE]:=4+(i shl 8); {select read plane }
{$I-}
BlockWrite(f,mem[Segment_Adr[pa]:0],PAGESIZE);
{$I+}
fehler:=fehler OR (IOResult<>0);
END;
{$I-}
Close(f);
{$I+}
fehler:=fehler OR (IOResult<>0);
port[$3ce]:=5; {restore old read-/write mode }
port[$3cf]:=oldMode;
IF fehler THEN Error:=Err_FileIO
END;
PROCEDURE LoadPage(name:STRING; pa:BYTE);
{ in: name = file name for the picture to load}
{ pa = destination page to be loaded with the picture (0..2) }
{ PAGESIZE = size of one bitplane }
{ PICHeader= tag for picture files }
{out: - }
{rem: The bitmap-picture in file "name" has been loaded into page "pa" }
VAR f:FILE;
i,oldMode:BYTE;
fehler:BOOLEAN;
s:STRING[3];
splane:WORD;
p1,p2:POINTER;
TYPE tempArray=ARRAY[1..PAGESIZE] OF BYTE;
VAR temp:^tempArray;
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber; exit
END;
{$I-}
Assign(f,name); fehler:=IOResult<>0;
Reset(f,1); fehler:=fehler OR (IOResult<>0);
s[0]:=PICHeader[0];
BlockRead(f,s[1],Length(PICHeader)); fehler:=fehler OR (IOResult<>0);
{$I+}
IF fehler
THEN BEGIN
{$I-} Close(f); {$I+}
Error:=Err_FileIO; exit
END
ELSE IF (FileSize(f)<>4*PAGESIZE+Length(PICHeader)) OR (s<>PICHeader)
THEN BEGIN
{$I-} Close(f); {$I+}
Error:=Err_NoPicture; exit
END;
ASM cli END;
port[$3ce]:=5; {save old read-/write mode }
oldMode:=port[$3cf];
New(temp);
ASM sti END;
FOR i:=0 TO 3 DO
BEGIN
{$I-}
BlockRead(f,temp^[1],PAGESIZE);
{$I+}
fehler:=fehler OR (IOResult<>0);
splane:=2+(TranslateTab[i] shl 8); {which write plane }
p1:=@temp^[1];
p2:=ptr(Segment_Adr[pa],0);
ASM
cli
mov dx,$3CE {select write mode 0 }
mov ax,$4005
out dx,ax
mov ax,splane {select write plane }
mov dx,$3C4
out dx,ax
les di,p2
lds si,p1
mov cx,PAGESIZE SHR 1
rep movsw
mov ax,SEG @DATA
mov ds,ax
sti
END;
END;
Dispose(temp);
{$I-}
Close(f);
{$I+}
fehler:=fehler OR (IOResult<>0);
portw[$3ce]:=oldMode SHL 8 + 5; {restore old read-/write mode }
IF fehler THEN Error:=Err_FileIO
END;
PROCEDURE WriteBackgroundPage(name:STRING);
{ in: name = file name for the background picture to store }
{ BACKGNDPAGE= page to be saved (=2) }
{ PAGESIZE = size of one bitplane }
{ PICHeader = tag for picture file }
{out: - }
{rem: The picture of the background page has been stored as file "name" }
{ This file has size 4*PAGESIZE+3 = 64003 bytes: 320x200 points }
{ at 1 byte each, plus length(PICHeader) as tag }
{ Using the routine only makes sense when using background mode STATIC }
BEGIN
WritePage(name,BACKGNDPAGE)
END;
PROCEDURE LoadBackgroundPage(name:STRING);
{ in: name = file name for the background picture to load}
{ BACKGNDPAGE= destination page, in which to load the picture (=2) }
{ PAGESIZE = size of one bitplane }
{ PICHeader= tag for picture files }
{out: - }
{rem: The bitmap-picture contained in file "name" has been loaded to the }
{ background page BACKGNDPAGE}
{ Using the routine only makes sense when using background mode STATIC}
BEGIN
LoadPage(name,BACKGNDPAGE)
END;
PROCEDURE FadeIn(pa,ti,style:WORD);
{ in: pa = page which shall be faded onto the actually visible page }
{ ti = time in milliseconds for this action }
{ style = algorithm which shall be used }
{ 1-PAGE = actually visible page }
{out: Error = Err_InvalidFade, if illegal "style" value has been used }
{rem: graphic mode must have been initialized already }
{ most often, you will use pa=BACKGNDPAGE }
PROCEDURE WipeIn(pa,time:WORD);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST hoehe =40; {divisor of Succ(YMAX)}
breite=40; {divisor of Succ(XMAX)}
br_x =Succ(XMAX) DIV breite; {blocks in X-direction}
br_y =Succ(YMAX) DIV hoehe; {blocks in Y-direction}
n=hoehe*br_x; {number of executions of the delay loop }
VAR i,x,y,ploty,plotx:WORD;
counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
p:POINTER;
BEGIN
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
FOR i:=0 TO pred(hoehe) DO
FOR x:=0 TO pred(br_x) DO
BEGIN
FOR y:=0 TO pred(br_y) DO
BEGIN
IF ODD(x)
THEN ploty:=y*hoehe+i +StartVirtualY
ELSE ploty:=y*hoehe+(hoehe-1-i)+StartVirtualY;
plotx:=x*breite +StartVirtualX;
p:=GetImage(plotx,ploty,plotx+pred(breite),ploty,pa);
PutImage(plotx,ploty,p,1-PAGE);
FreeImageMem(p);
END; {of FOR y}
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
END; {of FOR x}
END;
PROCEDURE Chaos(pa,time:WORD;m:BYTE);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ m = the way how this shall be done (1..14)}
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST n=Succ(XMAX)*Succ(YMAX); {number of screen pixels}
{e.g., good values are sums of powers of 2 +1 }
para:ARRAY[1..14] OF WORD=
(13477,65,337,129,257,513,769,1025,481,4097,5121,177,16385,16897);
VAR i,k,x,y:WORD;
counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
rand:WORD;
BEGIN
t:=ClockTicks;
counter:=0;
rand:=0;
IF (m<1) OR (m>14) THEN m:=1;
k:=para[m];
temp:=0.0182*time/n;
FOR i:=0 TO 65535 DO
BEGIN
ASM {compute: "x:=rand MOD 320" and "y:=rand DIV 320" }
XOR DX,DX
MOV AX,rand
MOV BX,XMAX+1
DIV BX
MOV y,AX
MOV x,DX
END;
IF y<=YMAX
THEN PutPixel(StartVirtualX+x,StartVirtualY+y,
PageGetPixel(StartVirtualX+x,StartVirtualY+y,pa));
ASM {compute: rand:=rand*k+1 }
MOV AX,rand
MUL k
INC AX
MOV rand,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
END; {of FOR i}
END;
PROCEDURE SweepVertical(pa,time:WORD; down:BOOLEAN);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ down = TRUE/FALSE for: from bottom to top/vice versa }
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST n=Succ(YMAX); {number of executions of the delay loop }
VAR y,counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
oldColor,step:BYTE;
p:POINTER;
BEGIN
oldColor:=Color;
Color:=white;
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
IF down
THEN FOR y:=0+StartVirtualY TO YMAX+StartVirtualY DO
BEGIN
Line(StartVirtualX,y,StartVirtualX+XMAX,y,1-PAGE);
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
p:=GetImage(StartVirtualX,y,StartVirtualX+XMAX,y,pa);
PutImage(StartVirtualX,y,p,1-PAGE);
FreeImageMem(p);
END
ELSE FOR y:=YMAX+StartVirtualY DOWNTO 0+StartVirtualY DO
BEGIN
Line(StartVirtualX,y,StartVirtualX+XMAX,y,1-PAGE);
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
p:=GetImage(StartVirtualX,y,StartVirtualX+XMAX,y,pa);
PutImage(StartVirtualX,y,p,1-PAGE);
FreeImageMem(p);
END;
Color:=oldColor
END;
PROCEDURE SweepHorizontal(pa,time:WORD; left_to_right:BOOLEAN);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ left_to_right=TRUE/FALSE for: from left to right/vice versa }
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST n=Succ(XMAX); {number of executions of the delay loop }
VAR x,counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
oldColor,step:BYTE;
p:POINTER;
BEGIN
oldColor:=Color;
Color:=white;
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
IF left_to_right
THEN FOR x:=0+StartVirtualX TO XMAX+StartVirtualX DO
BEGIN
Line(x,StartVirtualY,x,StartVirtualY+YMAX,1-PAGE);
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
p:=GetImage(x,StartVirtualY,x,StartVirtualY+YMAX,pa);
PutImage(x,StartVirtualY,p,1-PAGE);
FreeImageMem(p);
END
ELSE FOR x:=XMAX+StartVirtualX DOWNTO 0+StartVirtualX DO
BEGIN
Line(x,StartVirtualY,x,StartVirtualY+YMAX,1-PAGE);
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
p:=GetImage(x,StartVirtualY,x,StartVirtualY+YMAX,pa);
PutImage(x,StartVirtualY,p,1-PAGE);
FreeImageMem(p);
END;
Color:=oldColor
END;
PROCEDURE ScrollVertical(pa,time:WORD; up:BOOLEAN);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ up = TRUE/FALSE for: from bottom to top/vice versa }
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
LABEL oneLine1,oneLine2;
CONST n=Succ(YMAX); {number of executions of the delay loop }
VAR counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
BEGIN
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
IF up
THEN BEGIN {scroll upwards }
ASM
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES:=Segment_Adr[1-PAGE] = ^visible page }
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX:=Segment_Adr[pa] = ^source address}
PUSH DS
MOV DX,AX
MOV BX,YMAX*LINESIZE+(LINESIZE-1) {DX:BX = ^source data}
MOV AX,YMAX {AX = row counter }
oneLine2:
STD {move backwards! }
MOV SI,ES {first scroll old contents upwards: }
MOV DS,SI {DS=ES=visible graphic page }
MOV SI,(YMAX-1)*LINESIZE+(LINESIZE-1) {from last but one graphic row}
MOV DI,YMAX*LINESIZE+(LINESIZE-1) {to last graphic row }
MOV CX,YMAX*LINESIZE {199 rows }
REP MOVSB
MOV DS,DX {now make new row visible: }
MOV SI,BX {DS:SI=^row to move }
MOV CX,LINESIZE {1 row }
REP MOVSB
SUB BX,LINESIZE {increase source pointer }
{--- insertion to realize timing:}
PUSH AX {save all registers needed later }
PUSH BX
PUSH DX
PUSH ES
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
CLD {just to be sure! }
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
POP DX
POP BX
POP AX
{--- end of insertion}
DEC AX {all rows done? }
JNS oneLine2 {no, next row }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP DS
END;
END
ELSE BEGIN {scroll downwards }
ASM
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES:=Segment_Adr[1-PAGE] = ^visible page }
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX:=Segment_Adr[pa] = ^source address}
PUSH DS
MOV DX,AX
MOV BX,0*LINESIZE {DX:BX = ^source data}
MOV AX,YMAX {AX = row counter }
oneLine1:
MOV SI,ES {first scroll old contents upwards: }
MOV DS,SI {DS=ES=visible graphic page }
MOV SI,1*LINESIZE {from graphic row 1}
MOV DI,0*LINESIZE {to graphic row 0 }
MOV CX,YMAX*LINESIZE {199 rows }
REP MOVSB
MOV DS,DX {now make new row visible: }
MOV SI,BX {DS:SI=^row to move }
MOV CX,LINESIZE {1 row }
REP MOVSB
ADD BX,LINESIZE {increase source pointer }
{--- insertion to realize timing:}
PUSH AX {save all registers needed later }
PUSH BX
PUSH DX
PUSH ES
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
POP DX
POP BX
POP AX
{--- end of insertion}
DEC AX {all rows done? }
JNS oneLine1 {no, next row }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP DS
END;
END;
END;
PROCEDURE ScrollHorizontal(pa,time:WORD; left:BOOLEAN);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ left = TRUE/FALSE for: from left to right/vice versa }
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
LABEL oneColumn1,oneColumn2;
CONST n=Pred(LINESIZE); {number of executions of the delay loop }
VAR counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
BEGIN
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
IF left
THEN BEGIN {scroll to the left }
ASM
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES:=Segment_Adr[1-PAGE] = ^visible page }
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX:=Segment_Adr[pa] = ^source address}
PUSH DS
MOV DX,AX
MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
MOV AX,LINESIZE-1 {AX = column counter}
oneColumn2: {scroll old screen contents to the right:}
MOV SI,ES
MOV DS,SI {DS = ES = visible graphic page }
MOV SI,PAGESIZE-2
MOV DI,PAGESIZE-1
MOV CX,PAGESIZE-1
STD
REP MOVSB
CLD
MOV CX,SEG @DATA
MOV DS,CX {DS = ^TP's data}
MOV CX,YMAX+1 {CX = row counter }
MOV SI,AX
XOR DI,DI
MOV BX,LINESIZE-1
MOV DS,DX {DS = ^source data}
@oneRow: {update first column: }
MOVSB
ADD SI,BX {position at next row: }
ADD DI,BX {works, because BX+1=LINESIZE}
LOOP @oneRow
{--- insertion to realize timing:}
PUSH AX {save all registers needed later }
PUSH DX
PUSH ES
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
POP DX
POP AX
{--- end of insertion}
DEC AX {all columns done? }
JNS oneColumn2 {no, next column }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP DS
END;
END
ELSE BEGIN {scroll to the right }
ASM
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES:=Segment_Adr[1-PAGE] = ^visible page }
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX:=Segment_Adr[pa] = ^source address}
PUSH DS
MOV DX,AX
MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
MOV AX,0 {AX = column counter}
oneColumn1: {scroll old screen contents to the left:}
MOV SI,ES
MOV DS,SI {DS = ES = visible graphic page }
MOV SI,1
XOR DI,DI
MOV CX,PAGESIZE-1
REP MOVSB
MOV CX,SEG @DATA
MOV DS,CX {DS = ^TP's data}
MOV CX,YMAX+1 {CX = row counter }
MOV SI,AX
MOV DI,LINESIZE-1
MOV BX,LINESIZE-1
MOV DS,DX {DS = ^source data}
@oneRow: {update last column: }
MOVSB
ADD SI,BX {position at next row: }
ADD DI,BX {works, because BX+1=LINESIZE}
LOOP @oneRow
{--- insertion to realize timing:}
PUSH AX {save all registers needed later }
PUSH DX
PUSH ES
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
POP DX
POP AX
{--- end of insertion}
INC AX {all columns done? }
CMP AX,LINESIZE
JB oneColumn1 {no, next column }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP DS
END;
END;
END;
PROCEDURE CircleIn(pa,time:WORD);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST centerX=XMAX DIV 2; {middle of screen}
centerY=YMAX DIV 2;
k=189; {number of circles:= sqrt(centerX²+centerY²), rounded up}
adjust=0.707106781; {compensation in diagonal direction = 1/sqrt(2) }
n=TRUNC(k/adjust); {number of executions of the delay loop }
VAR radqu,x,y,x0,y0,u1,u2,u3,u4,v1,v2,v3,v4:WORD;
counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
radius,temp:REAL;
BEGIN
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
x0:=centerX + StartVirtualX;
y0:=centerY + StartVirtualY;
{unfortunately, FOR true_radius:=1 TO k STEP 1/adjust isn't possible in TP }
radius:=0.0;
REPEAT
radqu:=TRUNC(sqr(radius));
FOR x:=0 TO TRUNC(radius/sqrt(2)) DO {compute octant }
BEGIN
y:=TRUNC(sqrt(radqu-sqr(x))); {Pythagorean proposition}
u1:=x0-x; v1:=y0-y; {use axial- and point symmetrie }
u2:=x0+x; v2:=y0+y;
u3:=x0-y; v3:=y0-x;
u4:=x0+y; v4:=y0+x;
PutPixel(u1,v1,PageGetPixel(u1,v1,pa));
PutPixel(u1,v2,PageGetPixel(u1,v2,pa));
PutPixel(u2,v1,PageGetPixel(u2,v1,pa));
PutPixel(u2,v2,PageGetPixel(u2,v2,pa));
PutPixel(u3,v3,PageGetPixel(u3,v3,pa));
PutPixel(u3,v4,PageGetPixel(u3,v4,pa));
PutPixel(u4,v3,PageGetPixel(u4,v3,pa));
PutPixel(u4,v4,PageGetPixel(u4,v4,pa));
END;
radius:=radius+adjust;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
UNTIL radius>=k;
END;
BEGIN {of FadeIn}
IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
THEN Error:=Err_InvalidPageNumber
ELSE CASE style OF
Fade_Squares :WipeIn(pa,ti);
Fade_Moiree1..Fade_Moiree14:Chaos(pa,ti,style+1-Fade_Moiree1);
Fade_SweepInFromTop: SweepVertical(pa,ti,TRUE);
Fade_SweepInFromBottom: SweepVertical(pa,ti,FALSE);
Fade_SweepInFromLeft: SweepHorizontal(pa,ti,TRUE);
Fade_SweepInFromRight: SweepHorizontal(pa,ti,FALSE);
Fade_ScrollInFromTop: ScrollVertical(pa,ti,TRUE);
Fade_ScrollInFromBottom:ScrollVertical(pa,ti,FALSE);
Fade_ScrollInFromLeft: ScrollHorizontal(pa,ti,TRUE);
Fade_ScrollInFromRight: ScrollHorizontal(pa,ti,FALSE);
Fade_Circles : CircleIn(pa,ti);
else Error:=Err_InvalidFade
END;
END;
PROCEDURE InitRoutines;
{ in: - }
{out: SpriteN[],SPRITEAD[] have been initialized to "completely empty" }
{ NextSprite[] has been set in such a way that every sprite is its }
{ own successor}
{ PAGE, PAGEADR have been set to graphic page 0 }
{ BACKGNDADR has been set to the background page }
{ SCROLLADR has been set to the scrollable background page }
{ BACKGROUNDMODE has been set to STATIC }
{ tile #0 has been set as default tile for scrollable background }
{ StartVirtualX,StartVirtualY =0 (that is: virtual = absolute coord.)}
{ oldMode = old graphicmode }
{ Error has been set, if no VGA-card could be found in the system }
{ CycleTime = 0, that is: no min. time for animation cycle }
{ Color = 15, that is: white }
{ ActualColors = default color palette of mode $13 }
{ CRTAddress = port address of CRT-address register}
{ StatusReg = port address of state register }
{rem: This procedure should be called only once - namely in the very }
{ beginning - for initializing the whole package properly }
VAR i:WORD;
FUNCTION IsVGA:BOOLEAN;
BEGIN
WITH Regs DO
BEGIN
AX:=$1A00;
Intr($10,Regs);
IsVGA:=(AL=$1A) AND {VGA's identify-adapter-function supported? }
( (BL=7) OR (BL=8) ) {monochrome or color VGA - adapter}
END;
END;
BEGIN
IF IsVGA THEN Error:=Err_None
ELSE Error:=Err_NoVGA;
SetCycleTime(0);
fillchar(SpriteN,sizeof(SpriteN),0);
fillchar(SPRITEAD,sizeof(SPRITEAD),0);
FOR i:=0 TO LoadMAX DO NextSprite[i]:=i;
BACKGNDADR:=Segment_Adr[BACKGNDPAGE]; {segment address of background page }
PAGE:=0; {page to be drawn upon }
PAGEADR:=Segment_Adr[PAGE];
SCROLLADR:=Segment_Adr[SCROLLPAGE];
SetBackgroundMode(STATIC);
SetOffscreenTile(0);
StartVirtualX:=0; StartVirtualY:=0; {virtual = absolute coordinates }
Color:=white; {set actual drawing color to white }
regs.ah:=$f; intr($10,regs); oldMode:=regs.al;
ActualColors:=DefaultColors;
{SetShadowTab(Schatten) isn't needed, as the default values are set already}
ASM {see if we are using color-/monochorme display}
MOV DX,3CCh {use output register: }
IN AL,DX
TEST AL,1 {is it a color display? }
MOV DX,3D4h
JNZ @L1 {yes }
MOV DX,3B4h {no }
@L1: {DX=3B4h/3D4h = CRTAddress-register for monochrome/color}
MOV CRTAddress,DX
ADD DX,6 {DX=3BAh/3DAh = state register for monochrome/color }
MOV StatusReg,DX
END; {of ASM}
END;
PROCEDURE CloseRoutines;
{ in: oldMode = old graphicmode, to which we'll switch back }
{out: - }
BEGIN
regs.al:=oldMode; regs.ah:=0; intr($10,regs);
END;
FUNCTION GetErrorMessage:STRING;
{ in: Error = number of the occurred error }
{out: the error described in words}
BEGIN
CASE Error OF
Err_None:GetErrorMessage:='No Error';
Err_NotEnoughMemory:GetErrorMessage:='Not enough memory available on heap';
Err_FileIO:GetErrorMessage:='I/O-error with file';
Err_InvalidSpriteNumber:GetErrorMessage:='Invalid sprite number used';
Err_NoSprite:GetErrorMessage:='No (or corrupted) sprite file';
Err_InvalidPageNumber:GetErrorMessage:='Invalid page number used';
Err_NoVGA:GetErrorMessage:='No VGA-card found';
Err_NoPicture:GetErrorMessage:='No (or corrupted) picture file';
Err_InvalidPercentage:GetErrorMessage:='Percentage value must be 0..100';
Err_NoTile:GetErrorMessage:='No (or corrupted) tile/sprite file';
Err_InvalidTileNumber:GetErrorMessage:='Invalid tile number used';
Err_InvalidCoordinates:GetErrorMessage:='Invalid coordinates used';
Err_BackgroundToBig:GetErrorMessage:='Background to big for tile-buffer';
Err_InvalidMode:GetErrorMessage:='Only STATIC or SCROLLING allowed here';
Err_InvalidSpriteLoadNumber:GetErrorMessage:='Invalid spriteload number used';
Err_NoPalette:GetErrorMessage:='No (or corrupted) palette file';
Err_PaletteWontFit:GetErrorMessage:='Attempt to write beyond palette end';
Err_InvalidFade:GetErrorMessage:='Invalid fade style used';
ELSE GetErrorMessage:='Unknown error';
END;
END;
BEGIN
InitRoutines;
END.